From 0c5f526f8bd69de37ee7b6de05f45040394cd6f2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 11 Jun 2019 14:41:25 -0700 Subject: [PATCH] Remove the Postfix plugin (#7097) * Remove the postfix plugin. * Remove references to postfix plugin in code. * Remove reference to postfix plugin in docs. --- .travis.yml | 2 +- certbot-postfix/LICENSE.txt | 190 ----------- certbot-postfix/MANIFEST.in | 4 - certbot-postfix/README.rst | 23 -- certbot-postfix/certbot_postfix/__init__.py | 3 - certbot-postfix/certbot_postfix/constants.py | 63 ---- certbot-postfix/certbot_postfix/installer.py | 285 ---------------- certbot-postfix/certbot_postfix/postconf.py | 152 --------- .../certbot_postfix/tests/__init__.py | 1 - .../certbot_postfix/tests/installer_test.py | 316 ------------------ .../certbot_postfix/tests/postconf_test.py | 109 ------ .../certbot_postfix/tests/util_test.py | 205 ------------ certbot-postfix/certbot_postfix/util.py | 292 ---------------- certbot-postfix/docs/.gitignore | 1 - certbot-postfix/docs/Makefile | 20 -- certbot-postfix/docs/api.rst | 8 - certbot-postfix/docs/api/installer.rst | 5 - certbot-postfix/docs/api/postconf.rst | 5 - certbot-postfix/docs/conf.py | 190 ----------- certbot-postfix/docs/index.rst | 28 -- certbot-postfix/docs/make.bat | 36 -- certbot-postfix/local-oldest-requirements.txt | 3 - certbot-postfix/setup.cfg | 2 - certbot-postfix/setup.py | 64 ---- certbot/plugins/disco.py | 1 - docs/using.rst | 2 - tools/_venv_common.py | 1 - tools/install_and_test.py | 3 +- tox.cover.py | 6 +- tox.ini | 8 - 30 files changed, 4 insertions(+), 2024 deletions(-) delete mode 100644 certbot-postfix/LICENSE.txt delete mode 100644 certbot-postfix/MANIFEST.in delete mode 100644 certbot-postfix/README.rst delete mode 100644 certbot-postfix/certbot_postfix/__init__.py delete mode 100644 certbot-postfix/certbot_postfix/constants.py delete mode 100644 certbot-postfix/certbot_postfix/installer.py delete mode 100644 certbot-postfix/certbot_postfix/postconf.py delete mode 100644 certbot-postfix/certbot_postfix/tests/__init__.py delete mode 100644 certbot-postfix/certbot_postfix/tests/installer_test.py delete mode 100644 certbot-postfix/certbot_postfix/tests/postconf_test.py delete mode 100644 certbot-postfix/certbot_postfix/tests/util_test.py delete mode 100644 certbot-postfix/certbot_postfix/util.py delete mode 100644 certbot-postfix/docs/.gitignore delete mode 100644 certbot-postfix/docs/Makefile delete mode 100644 certbot-postfix/docs/api.rst delete mode 100644 certbot-postfix/docs/api/installer.rst delete mode 100644 certbot-postfix/docs/api/postconf.rst delete mode 100644 certbot-postfix/docs/conf.py delete mode 100644 certbot-postfix/docs/index.rst delete mode 100644 certbot-postfix/docs/make.bat delete mode 100644 certbot-postfix/local-oldest-requirements.txt delete mode 100644 certbot-postfix/setup.cfg delete mode 100644 certbot-postfix/setup.py diff --git a/.travis.yml b/.travis.yml index 1eee09898..13ee9cc88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ matrix: env: TOXENV=mypy <<: *not-on-master - python: "2.7" - env: TOXENV='py27-{acme,apache,certbot,dns,nginx,postfix}-oldest' + env: TOXENV='py27-{acme,apache,certbot,dns,nginx}-oldest' sudo: required services: docker <<: *not-on-master diff --git a/certbot-postfix/LICENSE.txt b/certbot-postfix/LICENSE.txt deleted file mode 100644 index c8314fd1c..000000000 --- a/certbot-postfix/LICENSE.txt +++ /dev/null @@ -1,190 +0,0 @@ - Copyright 2017 Electronic Frontier Foundation and others - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/certbot-postfix/MANIFEST.in b/certbot-postfix/MANIFEST.in deleted file mode 100644 index 273381403..000000000 --- a/certbot-postfix/MANIFEST.in +++ /dev/null @@ -1,4 +0,0 @@ -include LICENSE.txt -include README.rst -recursive-include certbot_postfix/testdata * -recursive-include certbot_postfix/docs * diff --git a/certbot-postfix/README.rst b/certbot-postfix/README.rst deleted file mode 100644 index 1ae9cb980..000000000 --- a/certbot-postfix/README.rst +++ /dev/null @@ -1,23 +0,0 @@ -========================== -Postfix plugin for Certbot -========================== - -Note: this MTA installer is in **developer beta**-- we appreciate any testing, feedback, or -feature requests for this plugin. - -To install this plugin, in the root of this repo, run:: - - python tools/venv.py - source venv/bin/activate - -You can use this installer with any `authenticator plugin -`_. -For instance, with the `standalone authenticator -`_, which requires no extra server -software, you might run:: - - sudo ./venv/bin/certbot run --standalone -i postfix -d - -To just install existing certs with this plugin, run:: - - sudo ./venv/bin/certbot install -i postfix --cert-path --key-path -d diff --git a/certbot-postfix/certbot_postfix/__init__.py b/certbot-postfix/certbot_postfix/__init__.py deleted file mode 100644 index 122c54bc6..000000000 --- a/certbot-postfix/certbot_postfix/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Certbot Postfix plugin.""" - -from certbot_postfix.installer import Installer diff --git a/certbot-postfix/certbot_postfix/constants.py b/certbot-postfix/certbot_postfix/constants.py deleted file mode 100644 index 40a263a53..000000000 --- a/certbot-postfix/certbot_postfix/constants.py +++ /dev/null @@ -1,63 +0,0 @@ -"""Postfix plugin constants.""" - -# pylint: disable=unused-import, no-name-in-module -from acme.magic_typing import Dict, Tuple, Union -# pylint: enable=unused-import, no-name-in-module - -MINIMUM_VERSION = (2, 11,) - -# If the value of a default VAR is a tuple, then the values which -# come LATER in the tuple are more strict/more secure. -# Certbot will default to the first value in the tuple, but will -# not override "more secure" settings. - -ACCEPTABLE_SERVER_SECURITY_LEVELS = ("may", "encrypt") -ACCEPTABLE_CLIENT_SECURITY_LEVELS = ("may", "encrypt", - "dane", "dane-only", - "fingerprint", - "verify", "secure") -ACCEPTABLE_CIPHER_LEVELS = ("medium", "high") - -# Exporting certain ciphers to prevent logjam: https://weakdh.org/sysadmin.html -EXCLUDE_CIPHERS = ("aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, " - "EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CBC3-SHA, KRB5-DES, CBC3-SHA",) - - -TLS_VERSIONS = ("SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2") -# Should NOT use SSLv2/3. -ACCEPTABLE_TLS_VERSIONS = ("TLSv1", "TLSv1.1", "TLSv1.2") - -# Variables associated with enabling opportunistic TLS. -TLS_SERVER_VARS = { - "smtpd_tls_security_level": ACCEPTABLE_SERVER_SECURITY_LEVELS, -} # type:Dict[str, Tuple[str, ...]] -TLS_CLIENT_VARS = { - "smtp_tls_security_level": ACCEPTABLE_CLIENT_SECURITY_LEVELS, -} # type:Dict[str, Tuple[str, ...]] -# Default variables for a secure MTA server [receiver]. -DEFAULT_SERVER_VARS = { - "smtpd_tls_auth_only": ("yes",), - "smtpd_tls_mandatory_protocols": ("!SSLv2, !SSLv3",), - "smtpd_tls_protocols": ("!SSLv2, !SSLv3",), - "smtpd_tls_ciphers": ACCEPTABLE_CIPHER_LEVELS, - "smtpd_tls_mandatory_ciphers": ACCEPTABLE_CIPHER_LEVELS, - "smtpd_tls_exclude_ciphers": EXCLUDE_CIPHERS, - "smtpd_tls_eecdh_grade": ("strong",), -} # type:Dict[str, Tuple[str, ...]] - -# Default variables for a secure MTA client [sender]. -DEFAULT_CLIENT_VARS = { - "smtp_tls_ciphers": ACCEPTABLE_CIPHER_LEVELS, - "smtp_tls_exclude_ciphers": EXCLUDE_CIPHERS, - "smtp_tls_mandatory_ciphers": ACCEPTABLE_CIPHER_LEVELS, -} # type:Dict[str, Tuple[str, ...]] - -CLI_DEFAULTS = dict( - config_dir="/etc/postfix", - ctl="postfix", - config_utility="postconf", - tls_only=False, - ignore_master_overrides=False, - server_only=False, -) -"""CLI defaults.""" diff --git a/certbot-postfix/certbot_postfix/installer.py b/certbot-postfix/certbot_postfix/installer.py deleted file mode 100644 index b5e4df5ec..000000000 --- a/certbot-postfix/certbot_postfix/installer.py +++ /dev/null @@ -1,285 +0,0 @@ -"""certbot installer plugin for postfix.""" -import logging - -import six -import zope.component -import zope.interface - -# pylint: disable=unused-import, no-name-in-module -from acme.magic_typing import Callable, Dict, List -# pylint: enable=unused-import, no-name-in-module - -from certbot import errors -from certbot import interfaces -from certbot import util as certbot_util -from certbot.compat import os -from certbot.plugins import common as plugins_common - -from certbot_postfix import constants -from certbot_postfix import postconf -from certbot_postfix import util - -logger = logging.getLogger(__name__) - -@zope.interface.implementer(interfaces.IInstaller) -@zope.interface.provider(interfaces.IPluginFactory) -class Installer(plugins_common.Installer): - """Certbot installer plugin for Postfix. - - :ivar str config_dir: Postfix configuration directory to modify - :ivar list save_notes: documentation for proposed changes. This is - cleared and stored in Certbot checkpoints when save() is called - - :ivar postconf: Wrapper for Postfix configuration command-line tool. - :type postconf: :class: `certbot_postfix.postconf.ConfigMain` - :ivar postfix: Wrapper for Postfix command-line tool. - :type postfix: :class: `certbot_postfix.util.PostfixUtil` - """ - - description = "Configure TLS with the Postfix MTA" - - @classmethod - def add_parser_arguments(cls, add): - add("ctl", default=constants.CLI_DEFAULTS["ctl"], - help="Path to the 'postfix' control program.") - # This directory points to Postfix's configuration directory. - add("config-dir", default=constants.CLI_DEFAULTS["config_dir"], - help="Path to the directory containing the " - "Postfix main.cf file to modify instead of using the " - "default configuration paths.") - add("config-utility", default=constants.CLI_DEFAULTS["config_utility"], - help="Path to the 'postconf' executable.") - add("tls-only", action="store_true", default=constants.CLI_DEFAULTS["tls_only"], - help="Only set params to enable opportunistic TLS and install certificates.") - add("server-only", action="store_true", default=constants.CLI_DEFAULTS["server_only"], - help="Only set server params (prefixed with smtpd*)") - add("ignore-master-overrides", action="store_true", - default=constants.CLI_DEFAULTS["ignore_master_overrides"], - help="Ignore errors reporting overridden TLS parameters in master.cf.") - - def __init__(self, *args, **kwargs): - super(Installer, self).__init__(*args, **kwargs) - # Wrapper around postconf commands - self.postfix = None - self.postconf = None - - # Files to save - self.save_notes = [] # type: List[str] - - self._enhance_func = {} # type: Dict[str, Callable[[str, str], None]] - # Since we only need to enable TLS once for all domains, - # keep track of whether this enhancement was already called. - self._tls_enabled = False - - def prepare(self): - """Prepare the installer. - - :raises errors.PluginError: when an unexpected error occurs - :raises errors.MisconfigurationError: when the config is invalid - :raises errors.NoInstallationError: when can't find installation - :raises errors.NotSupportedError: when version is not supported - """ - # Verify postfix and postconf are installed - for param in ("ctl", "config_utility",): - util.verify_exe_exists(self.conf(param), - "Cannot find executable '{0}'. You can provide the " - "path to this command with --{1}".format( - self.conf(param), - self.option_name(param))) - - # Set up CLI tools - self.postfix = util.PostfixUtil(self.conf('config-dir')) - self.postconf = postconf.ConfigMain(self.conf('config-utility'), - self.conf('ignore-master-overrides'), - self.conf('config-dir')) - - # Ensure current configuration is valid. - self.config_test() - - # Check Postfix version - self._check_version() - self._lock_config_dir() - self.install_ssl_dhparams() - - def config_test(self): - """Test to see that the current Postfix configuration is valid. - - :raises errors.MisconfigurationError: If the configuration is invalid. - """ - self.postfix.test() - - def _check_version(self): - """Verifies that the installed Postfix version is supported. - - :raises errors.NotSupportedError: if the version is unsupported - """ - if self._get_version() < constants.MINIMUM_VERSION: - version_string = '.'.join([str(n) for n in constants.MINIMUM_VERSION]) - raise errors.NotSupportedError('Postfix version must be at least %s' % version_string) - - def _lock_config_dir(self): - """Stop two Postfix plugins from modifying the config at once. - - :raises .PluginError: if unable to acquire the lock - """ - try: - certbot_util.lock_dir_until_exit(self.conf('config-dir')) - except (OSError, errors.LockError): - logger.debug("Encountered error:", exc_info=True) - raise errors.PluginError( - "Unable to lock %s" % self.conf('config-dir')) - - def more_info(self): - """Human-readable string to help the user. Describes steps taken and any relevant - info to help the user decide which plugin to use. - - :rtype: str - """ - return ( - "Configures Postfix to try to authenticate mail servers, use " - "installed certificates and disable weak ciphers and protocols.{0}" - "Server root: {root}{0}" - "Version: {version}".format( - os.linesep, - root=self.conf('config-dir'), - version='.'.join([str(i) for i in self._get_version()])) - ) - - def _get_version(self): - """Return the version of Postfix, as a tuple. (e.g. '2.11.3' is (2, 11, 3)) - - :returns: version - :rtype: tuple - - :raises errors.PluginError: Unable to find Postfix version. - """ - mail_version = self.postconf.get_default("mail_version") - return tuple(int(i) for i in mail_version.split('.')) - - def get_all_names(self): - """Returns all names that may be authenticated. - - :rtype: `set` of `str` - - """ - return certbot_util.get_filtered_names(self.postconf.get(var) - for var in ('mydomain', 'myhostname', 'myorigin',)) - - def _set_vars(self, var_dict): - """Sets all parameters in var_dict to config file. If current value is already set - as more secure (acceptable), then don't set/overwrite it. - """ - for param, acceptable in six.iteritems(var_dict): - if not util.is_acceptable_value(param, self.postconf.get(param), acceptable): - self.postconf.set(param, acceptable[0], acceptable) - - def _confirm_changes(self): - """Confirming outstanding updates for configuration parameters. - - :raises errors.PluginError: when user rejects the configuration changes. - """ - updates = self.postconf.get_changes() - output_string = "Postfix TLS configuration parameters to update in main.cf:\n" - for name, value in six.iteritems(updates): - output_string += "{0} = {1}\n".format(name, value) - output_string += "Is this okay?\n" - if not zope.component.getUtility(interfaces.IDisplay).yesno(output_string, - force_interactive=True, default=True): - raise errors.PluginError( - "Manually rejected configuration changes.\n" - "Try using --tls-only or --server-only to change a particular" - "subset of configuration parameters.") - - def deploy_cert(self, domain, cert_path, - key_path, chain_path, fullchain_path): # pylint: disable=unused-argument - """Configure the Postfix SMTP server to use the given TLS cert. - - :param str domain: domain to deploy certificate file - :param str cert_path: absolute path to the certificate file - :param str key_path: absolute path to the private key file - :param str chain_path: absolute path to the certificate chain file - :param str fullchain_path: absolute path to the certificate fullchain - file (cert plus chain) - - :raises .PluginError: when cert cannot be deployed - - """ - if self._tls_enabled: - return - self._tls_enabled = True - self.save_notes.append("Configuring TLS for {0}".format(domain)) - self.postconf.set("smtpd_tls_cert_file", cert_path) - self.postconf.set("smtpd_tls_key_file", key_path) - self._set_vars(constants.TLS_SERVER_VARS) - if not self.conf('server_only'): - self._set_vars(constants.TLS_CLIENT_VARS) - if not self.conf('tls_only'): - self._set_vars(constants.DEFAULT_SERVER_VARS) - if not self.conf('server_only'): - self._set_vars(constants.DEFAULT_CLIENT_VARS) - # Despite the name, this option also supports 2048-bit DH params. - # http://www.postfix.org/FORWARD_SECRECY_README.html#server_fs - self.postconf.set("smtpd_tls_dh1024_param_file", self.ssl_dhparams) - self._confirm_changes() - - def enhance(self, domain, enhancement, options=None): # pylint: disable=unused-argument - """Raises an exception since this installer doesn't support any enhancements. - """ - raise errors.PluginError( - "Unsupported enhancement: {0}".format(enhancement)) - - def supported_enhancements(self): - """Returns a list of supported enhancements. - - :rtype: list - - """ - return [] - - def save(self, title=None, temporary=False): - """Creates backups and writes changes to configuration files. - - :param str title: The title of the save. If a title is given, the - configuration will be saved as a new checkpoint and put in a - timestamped directory. `title` has no effect if temporary is true. - - :param bool temporary: Indicates whether the changes made will - be quickly reversed in the future (challenges) - - :raises errors.PluginError: when save is unsuccessful - """ - save_files = set((os.path.join(self.conf('config-dir'), "main.cf"),)) - self.add_to_checkpoint(save_files, - "\n".join(self.save_notes), temporary) - self.postconf.flush() - - del self.save_notes[:] - - if title and not temporary: - self.finalize_checkpoint(title) - - def recovery_routine(self): - super(Installer, self).recovery_routine() - self.postconf = postconf.ConfigMain(self.conf('config-utility'), - self.conf('ignore-master-overrides'), - self.conf('config-dir')) - - def rollback_checkpoints(self, rollback=1): - """Rollback saved checkpoints. - - :param int rollback: Number of checkpoints to revert - - :raises .errors.PluginError: If there is a problem with the input or - the function is unable to correctly revert the configuration - """ - super(Installer, self).rollback_checkpoints(rollback) - self.postconf = postconf.ConfigMain(self.conf('config-utility'), - self.conf('ignore-master-overrides'), - self.conf('config-dir')) - - def restart(self): - """Restart or refresh the server content. - - :raises .PluginError: when server cannot be restarted - """ - self.postfix.restart() diff --git a/certbot-postfix/certbot_postfix/postconf.py b/certbot-postfix/certbot_postfix/postconf.py deleted file mode 100644 index efa208597..000000000 --- a/certbot-postfix/certbot_postfix/postconf.py +++ /dev/null @@ -1,152 +0,0 @@ -"""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 {0} is not a valid Postfix parameter name.".format(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 not self._updated: - 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: {0}".format(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() diff --git a/certbot-postfix/certbot_postfix/tests/__init__.py b/certbot-postfix/certbot_postfix/tests/__init__.py deleted file mode 100644 index 7316b5888..000000000 --- a/certbot-postfix/certbot_postfix/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -""" Certbot Postfix Tests """ diff --git a/certbot-postfix/certbot_postfix/tests/installer_test.py b/certbot-postfix/certbot_postfix/tests/installer_test.py deleted file mode 100644 index a24643379..000000000 --- a/certbot-postfix/certbot_postfix/tests/installer_test.py +++ /dev/null @@ -1,316 +0,0 @@ -"""Tests for certbot_postfix.installer.""" -import copy -import functools -import unittest -from contextlib import contextmanager - -import mock -import pkg_resources -import six -from acme.magic_typing import Dict, Tuple # pylint: disable=unused-import,no-name-in-module - -from certbot import errors -from certbot.compat import os -from certbot.tests import util as certbot_test_util - - -DEFAULT_MAIN_CF = { - "smtpd_tls_cert_file": "", - "smtpd_tls_key_file": "", - "smtpd_tls_dh1024_param_file": "", - "smtpd_tls_security_level": "none", - "smtpd_tls_auth_only": "", - "smtpd_tls_mandatory_protocols": "", - "smtpd_tls_protocols": "", - "smtpd_tls_ciphers": "", - "smtpd_tls_exclude_ciphers": "", - "smtpd_tls_mandatory_ciphers": "", - "smtpd_tls_eecdh_grade": "medium", - "smtp_tls_security_level": "", - "smtp_tls_ciphers": "", - "smtp_tls_exclude_ciphers": "", - "smtp_tls_mandatory_ciphers": "", - "mail_version": "3.2.3" -} - -def _main_cf_with(obj): - main_cf = copy.copy(DEFAULT_MAIN_CF) - main_cf.update(obj) - return main_cf - -class InstallerTest(certbot_test_util.ConfigTestCase): - # pylint: disable=too-many-public-methods - - def setUp(self): - super(InstallerTest, self).setUp() - _config_file = pkg_resources.resource_filename("certbot_postfix.tests", - os.path.join("testdata", "config.json")) - self.config.postfix_ctl = "postfix" - self.config.postfix_config_dir = self.tempdir - self.config.postfix_config_utility = "postconf" - self.config.postfix_tls_only = False - self.config.postfix_server_only = False - self.config.config_dir = self.tempdir - - @mock.patch("certbot_postfix.installer.util.is_acceptable_value") - def test_set_vars(self, mock_is_acceptable_value): - mock_is_acceptable_value.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - mock_is_acceptable_value.return_value = False - - @mock.patch("certbot_postfix.installer.util.is_acceptable_value") - def test_acceptable_value(self, mock_is_acceptable_value): - mock_is_acceptable_value.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - mock_is_acceptable_value.return_value = False - - @certbot_test_util.patch_get_utility() - def test_confirm_changes_no_raises_error(self, mock_util): - mock_util().yesno.return_value = False - with create_installer(self.config) as installer: - installer.prepare() - self.assertRaises(errors.PluginError, installer.deploy_cert, - "example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - - @certbot_test_util.patch_get_utility() - def test_save(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.postconf.flush = mock.Mock() - installer.reverter = mock.Mock() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - installer.save() - self.assertEqual(installer.save_notes, []) - self.assertEqual(installer.postconf.flush.call_count, 1) - self.assertEqual(installer.reverter.add_to_checkpoint.call_count, 1) - - @certbot_test_util.patch_get_utility() - def test_save_with_title(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.postconf.flush = mock.Mock() - installer.reverter = mock.Mock() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - installer.save(title="new_file!") - self.assertEqual(installer.reverter.finalize_checkpoint.call_count, 1) - - @certbot_test_util.patch_get_utility() - def test_rollback_checkpoints_resets_postconf(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - installer.rollback_checkpoints() - self.assertEqual(installer.postconf.get_changes(), {}) - - @certbot_test_util.patch_get_utility() - def test_recovery_routine_resets_postconf(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - installer.recovery_routine() - self.assertEqual(installer.postconf.get_changes(), {}) - - def test_restart(self): - with create_installer(self.config) as installer: - installer.prepare() - installer.restart() - self.assertEqual(installer.postfix.restart.call_count, 1) # pylint: disable=no-member - - def test_add_parser_arguments(self): - options = set(("ctl", "config-dir", "config-utility", - "tls-only", "server-only", "ignore-master-overrides")) - mock_add = mock.MagicMock() - - from certbot_postfix import installer - installer.Installer.add_parser_arguments(mock_add) - - for call in mock_add.call_args_list: - self.assertTrue(call[0][0] in options) - - def test_no_postconf_prepare(self): - with create_installer(self.config) as installer: - installer_path = "certbot_postfix.installer" - exe_exists_path = installer_path + ".certbot_util.exe_exists" - path_surgery_path = "certbot_postfix.util.plugins_util.path_surgery" - with mock.patch(path_surgery_path, return_value=False): - with mock.patch(exe_exists_path, return_value=False): - self.assertRaises(errors.NoInstallationError, - installer.prepare) - - def test_old_version(self): - with create_installer(self.config, main_cf=_main_cf_with({"mail_version": "0.0.1"}))\ - as installer: - self.assertRaises(errors.NotSupportedError, installer.prepare) - - def test_lock_error(self): - with create_installer(self.config) as installer: - assert_raises = functools.partial(self.assertRaises, - errors.PluginError, - installer.prepare) - certbot_test_util.lock_and_call(assert_raises, self.tempdir) - - - @mock.patch('certbot.util.lock_dir_until_exit') - def test_dir_locked(self, lock_dir): - with create_installer(self.config) as installer: - lock_dir.side_effect = errors.LockError - self.assertRaises(errors.PluginError, installer.prepare) - - def test_more_info(self): - with create_installer(self.config) as installer: - installer.prepare() - output = installer.more_info() - self.assertTrue("Postfix" in output) - self.assertTrue(self.tempdir in output) - self.assertTrue(DEFAULT_MAIN_CF["mail_version"] in output) - - def test_get_all_names(self): - config = {"mydomain": "example.org", - "myhostname": "mail.example.org", - "myorigin": "example.org"} - with create_installer(self.config, main_cf=_main_cf_with(config)) as installer: - installer.prepare() - result = installer.get_all_names() - self.assertEqual(result, set(config.values())) - - @certbot_test_util.patch_get_utility() - def test_deploy(self, mock_util): - mock_util().yesno.return_value = True - from certbot_postfix import constants - with create_installer(self.config) as installer: - installer.prepare() - - # pylint: disable=protected-access - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - changes = installer.postconf.get_changes() - expected = {} # type: Dict[str, Tuple[str, ...]] - expected.update(constants.TLS_SERVER_VARS) - expected.update(constants.DEFAULT_SERVER_VARS) - expected.update(constants.DEFAULT_CLIENT_VARS) - self.assertEqual(changes["smtpd_tls_key_file"], "key_path") - self.assertEqual(changes["smtpd_tls_cert_file"], "cert_path") - for name, value in six.iteritems(expected): - self.assertEqual(changes[name], value[0]) - - @certbot_test_util.patch_get_utility() - def test_tls_only(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.conf = lambda x: x == "tls_only" - installer.postconf.set = mock.Mock() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - self.assertEqual(installer.postconf.set.call_count, 4) - - @certbot_test_util.patch_get_utility() - def test_server_only(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.conf = lambda x: x == "server_only" - installer.postconf.set = mock.Mock() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - self.assertEqual(installer.postconf.set.call_count, 11) - - @certbot_test_util.patch_get_utility() - def test_tls_and_server_only(self, mock_util): - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - installer.conf = lambda x: True - installer.postconf.set = mock.Mock() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - self.assertEqual(installer.postconf.set.call_count, 3) - - @certbot_test_util.patch_get_utility() - def test_deploy_twice(self, mock_util): - # Deploying twice on the same installer shouldn't do anything! - mock_util().yesno.return_value = True - with create_installer(self.config) as installer: - installer.prepare() - from certbot_postfix.postconf import ConfigMain - with mock.patch.object(ConfigMain, "set", wraps=installer.postconf.set) as fake_set: - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - self.assertEqual(fake_set.call_count, 15) - fake_set.reset_mock() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - self.assertFalse(fake_set.called) - - @certbot_test_util.patch_get_utility() - def test_deploy_already_secure(self, mock_util): - # Should not overwrite "more-secure" parameters - mock_util().yesno.return_value = True - more_secure = { - "smtpd_tls_security_level": "encrypt", - "smtpd_tls_protocols": "!SSLv3, !SSLv2, !TLSv1", - "smtpd_tls_eecdh_grade": "strong" - } - with create_installer(self.config,\ - main_cf=_main_cf_with(more_secure)) as installer: - installer.prepare() - installer.deploy_cert("example.com", "cert_path", "key_path", - "chain_path", "fullchain_path") - for param in more_secure: - self.assertFalse(param in installer.postconf.get_changes()) - - def test_enhance(self): - with create_installer(self.config) as installer: - installer.prepare() - self.assertRaises(errors.PluginError, - installer.enhance, - "example.org", "redirect") - - def test_supported_enhancements(self): - with create_installer(self.config) as installer: - installer.prepare() - self.assertEqual(installer.supported_enhancements(), []) - - -@contextmanager -def create_installer(config, main_cf=None): - """Creates a Postfix installer with calls to `postconf` and `postfix` mocked out. - - In particular, creates a ConfigMain object that does regular things, but seeds it - with values from `main_cf` and `master_cf` dicts. - """ - if main_cf is None: - main_cf = DEFAULT_MAIN_CF - - from certbot_postfix.postconf import ConfigMain - from certbot_postfix import installer - - def _mock_init_postconf(postconf, executable, ignore_master_overrides=False, config_dir=None): - # pylint: disable=protected-access,unused-argument - postconf._ignore_master_overrides = ignore_master_overrides - postconf._db = main_cf - postconf._master_db = {} - postconf._updated = {} - # override get_default to get from main - postconf.get_default = lambda name: main_cf[name] - with mock.patch.object(ConfigMain, "__init__", _mock_init_postconf): - exe_exists_path = "certbot_postfix.installer.certbot_util.exe_exists" - with mock.patch(exe_exists_path, return_value=True): - with mock.patch("certbot_postfix.installer.util.PostfixUtil", - return_value=mock.Mock()): - yield installer.Installer(config, "postfix") - - -if __name__ == "__main__": - unittest.main() # pragma: no cover diff --git a/certbot-postfix/certbot_postfix/tests/postconf_test.py b/certbot-postfix/certbot_postfix/tests/postconf_test.py deleted file mode 100644 index c042093d0..000000000 --- a/certbot-postfix/certbot_postfix/tests/postconf_test.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Tests for certbot_postfix.postconf.""" - -import unittest - -import mock - -from certbot import errors - - -class PostConfTest(unittest.TestCase): - """Tests for certbot_postfix.util.PostConf.""" - def setUp(self): - from certbot_postfix.postconf import ConfigMain - super(PostConfTest, self).setUp() - with mock.patch('certbot_postfix.util.PostfixUtilBase._get_output') as mock_call: - with mock.patch('certbot_postfix.postconf.ConfigMain._get_output_master') as \ - mock_master_call: - with mock.patch('certbot_postfix.postconf.util.verify_exe_exists') as verify_exe: - verify_exe.return_value = True - mock_call.return_value = ('default_parameter = value\n' - 'extra_param =\n' - 'overridden_by_master = default\n') - mock_master_call.return_value = ( - 'service/type/overridden_by_master = master_value\n' - 'service2/type/overridden_by_master = master_value2\n' - ) - self.config = ConfigMain('postconf', False) - - @mock.patch('certbot_postfix.util.PostfixUtilBase._get_output') - @mock.patch('certbot_postfix.postconf.util.verify_exe_exists') - def test_get_output_master(self, mock_verify_exe, mock_get_output): - from certbot_postfix.postconf import ConfigMain - mock_verify_exe.return_value = True - ConfigMain('postconf', lambda x, y, z: None) - mock_get_output.assert_called_with('-P') - - @mock.patch('certbot_postfix.util.PostfixUtilBase._get_output') - def test_read_default(self, mock_get_output): - mock_get_output.return_value = 'param = default_value' - self.assertEqual(self.config.get_default('param'), 'default_value') - - @mock.patch('certbot_postfix.util.PostfixUtilBase._call') - def test_set(self, mock_call): - self.config.set('extra_param', 'other_value') - self.assertEqual(self.config.get('extra_param'), 'other_value') - self.config.flush() - mock_call.assert_called_with(['-e', 'extra_param=other_value']) - - def test_set_bad_param_name(self): - self.assertRaises(KeyError, self.config.set, 'nonexistent_param', 'some_value') - - @mock.patch('certbot_postfix.util.PostfixUtilBase._call') - def test_write_revert(self, mock_call): - self.config.set('default_parameter', 'fake_news') - # revert config set - self.config.set('default_parameter', 'value') - self.config.flush() - mock_call.assert_not_called() - - @mock.patch('certbot_postfix.util.PostfixUtilBase._call') - def test_write_default(self, mock_call): - self.config.set('default_parameter', 'value') - self.config.flush() - mock_call.assert_not_called() - - def test_master_overrides(self): - self.assertEqual(self.config.get_master_overrides('overridden_by_master'), - [('service/type', 'master_value'), - ('service2/type', 'master_value2')]) - - def test_set_check_override(self): - self.assertRaises(errors.PluginError, self.config.set, - 'overridden_by_master', 'new_value') - - def test_ignore_check_override(self): - # pylint: disable=protected-access - self.config._ignore_master_overrides = True - self.config.set('overridden_by_master', 'new_value') - - def test_check_acceptable_overrides(self): - self.config.set('overridden_by_master', 'new_value', - ('master_value', 'master_value2')) - - @mock.patch('certbot_postfix.util.PostfixUtilBase._get_output') - def test_flush(self, mock_out): - self.config.set('default_parameter', 'new_value') - self.config.set('extra_param', 'another_value') - self.config.flush() - arguments = mock_out.call_args_list[-1][0][0] - self.assertEqual('-e', arguments[0]) - self.assertTrue('default_parameter=new_value' in arguments) - self.assertTrue('extra_param=another_value' in arguments) - - @mock.patch('certbot_postfix.util.PostfixUtilBase._get_output') - def test_flush_updates_object(self, mock_out): - self.config.set('default_parameter', 'new_value') - self.config.flush() - mock_out.reset_mock() - self.config.set('default_parameter', 'new_value') - mock_out.assert_not_called() - - @mock.patch('certbot_postfix.util.PostfixUtilBase._get_output') - def test_flush_throws_error_on_fail(self, mock_out): - mock_out.side_effect = [IOError("oh no!")] - self.config.set('default_parameter', 'new_value') - self.assertRaises(errors.PluginError, self.config.flush) - -if __name__ == '__main__': # pragma: no cover - unittest.main() diff --git a/certbot-postfix/certbot_postfix/tests/util_test.py b/certbot-postfix/certbot_postfix/tests/util_test.py deleted file mode 100644 index fa38f83ab..000000000 --- a/certbot-postfix/certbot_postfix/tests/util_test.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Tests for certbot_postfix.util.""" - -import subprocess -import unittest - -import mock - -from certbot import errors - - -class PostfixUtilBaseTest(unittest.TestCase): - """Tests for certbot_postfix.util.PostfixUtilBase.""" - - @classmethod - def _create_object(cls, *args, **kwargs): - from certbot_postfix.util import PostfixUtilBase - return PostfixUtilBase(*args, **kwargs) - - @mock.patch('certbot_postfix.util.verify_exe_exists') - def test_no_exe(self, mock_verify): - expected_error = errors.NoInstallationError - mock_verify.side_effect = expected_error - self.assertRaises(expected_error, self._create_object, 'nonexistent') - - def test_object_creation(self): - with mock.patch('certbot_postfix.util.verify_exe_exists'): - self._create_object('existent') - - @mock.patch('certbot_postfix.util.check_all_output') - def test_call_extends_args(self, mock_output): - # pylint: disable=protected-access - with mock.patch('certbot_postfix.util.verify_exe_exists'): - mock_output.return_value = 'expected' - postfix = self._create_object('executable') - postfix._call(['many', 'extra', 'args']) - mock_output.assert_called_with(['executable', 'many', 'extra', 'args']) - postfix._call() - mock_output.assert_called_with(['executable']) - - def test_create_with_config(self): - # pylint: disable=protected-access - with mock.patch('certbot_postfix.util.verify_exe_exists'): - postfix = self._create_object('exec', 'config_dir') - self.assertEqual(postfix._base_command, ['exec', '-c', 'config_dir']) - -class PostfixUtilTest(unittest.TestCase): - def setUp(self): - # pylint: disable=protected-access - from certbot_postfix.util import PostfixUtil - with mock.patch('certbot_postfix.util.verify_exe_exists'): - self.postfix = PostfixUtil() - self.postfix._call = mock.Mock() - self.mock_call = self.postfix._call - - def test_test(self): - self.postfix.test() - self.mock_call.assert_called_with(['check']) - - def test_test_raises_error_when_check_fails(self): - self.mock_call.side_effect = [subprocess.CalledProcessError(1, "")] - self.assertRaises(errors.MisconfigurationError, self.postfix.test) - self.mock_call.assert_called_with(['check']) - - def test_restart_while_running(self): - self.mock_call.side_effect = [subprocess.CalledProcessError(1, ""), None] - self.postfix.restart() - self.mock_call.assert_called_with(['start']) - - def test_restart_while_not_running(self): - self.postfix.restart() - self.mock_call.assert_called_with(['reload']) - - def test_restart_raises_error_when_reload_fails(self): - self.mock_call.side_effect = [None, subprocess.CalledProcessError(1, "")] - self.assertRaises(errors.PluginError, self.postfix.restart) - self.mock_call.assert_called_with(['reload']) - - def test_restart_raises_error_when_start_fails(self): - self.mock_call.side_effect = [ - subprocess.CalledProcessError(1, ""), - subprocess.CalledProcessError(1, "")] - self.assertRaises(errors.PluginError, self.postfix.restart) - self.mock_call.assert_called_with(['start']) - -class CheckAllOutputTest(unittest.TestCase): - """Tests for certbot_postfix.util.check_all_output.""" - - @classmethod - def _call(cls, *args, **kwargs): - from certbot_postfix.util import check_all_output - return check_all_output(*args, **kwargs) - - @mock.patch('certbot_postfix.util.logger') - @mock.patch('certbot_postfix.util.subprocess.Popen') - def test_command_error(self, mock_popen, mock_logger): - command = 'foo' - retcode = 42 - output = 'bar' - err = 'baz' - - mock_popen().communicate.return_value = (output, err) - mock_popen().poll.return_value = 42 - - self.assertRaises(subprocess.CalledProcessError, self._call, command) - log_args = mock_logger.debug.call_args[0] - for value in (command, retcode, output, err,): - self.assertTrue(value in log_args) - - @mock.patch('certbot_postfix.util.subprocess.Popen') - def test_success(self, mock_popen): - command = 'foo' - expected = ('bar', '') - mock_popen().communicate.return_value = expected - mock_popen().poll.return_value = 0 - - self.assertEqual(self._call(command), expected) - - def test_stdout_error(self): - self.assertRaises(ValueError, self._call, stdout=None) - - def test_stderr_error(self): - self.assertRaises(ValueError, self._call, stderr=None) - - def test_universal_newlines_error(self): - self.assertRaises(ValueError, self._call, universal_newlines=False) - - -class VerifyExeExistsTest(unittest.TestCase): - """Tests for certbot_postfix.util.verify_exe_exists.""" - - @classmethod - def _call(cls, *args, **kwargs): - from certbot_postfix.util import verify_exe_exists - return verify_exe_exists(*args, **kwargs) - - @mock.patch('certbot_postfix.util.certbot_util.exe_exists') - @mock.patch('certbot_postfix.util.plugins_util.path_surgery') - def test_failure(self, mock_exe_exists, mock_path_surgery): - mock_exe_exists.return_value = mock_path_surgery.return_value = False - self.assertRaises(errors.NoInstallationError, self._call, 'foo') - - @mock.patch('certbot_postfix.util.certbot_util.exe_exists') - def test_simple_success(self, mock_exe_exists): - mock_exe_exists.return_value = True - self._call('foo') - - @mock.patch('certbot_postfix.util.certbot_util.exe_exists') - @mock.patch('certbot_postfix.util.plugins_util.path_surgery') - def test_successful_surgery(self, mock_exe_exists, mock_path_surgery): - mock_exe_exists.return_value = False - mock_path_surgery.return_value = True - self._call('foo') - -class TestUtils(unittest.TestCase): - """ Testing random utility functions in util.py - """ - def test_report_master_overrides(self): - from certbot_postfix.util import report_master_overrides - self.assertRaises(errors.PluginError, report_master_overrides, 'name', - [('service/type', 'value')]) - # Shouldn't raise error - report_master_overrides('name', [('service/type', 'value')], - acceptable_overrides=('value',)) - - def test_no_acceptable_value(self): - from certbot_postfix.util import is_acceptable_value - self.assertFalse(is_acceptable_value('name', 'value', None)) - - def test_is_acceptable_value(self): - from certbot_postfix.util import is_acceptable_value - self.assertTrue(is_acceptable_value('name', 'value', ('value',))) - self.assertFalse(is_acceptable_value('name', 'bad', ('value',))) - - def test_is_acceptable_tuples(self): - from certbot_postfix.util import is_acceptable_value - self.assertTrue(is_acceptable_value('name', 'value', ('value', 'value1'))) - self.assertFalse(is_acceptable_value('name', 'bad', ('value', 'value1'))) - - def test_is_acceptable_protocols(self): - from certbot_postfix.util import is_acceptable_value - # SSLv2 and SSLv3 are both not supported, unambiguously - self.assertFalse(is_acceptable_value('tls_mandatory_protocols_lol', - 'SSLv2, SSLv3', None)) - self.assertFalse(is_acceptable_value('tls_protocols_lol', - 'SSLv2, SSLv3', None)) - self.assertFalse(is_acceptable_value('tls_protocols_lol', - '!SSLv2, !TLSv1', None)) - self.assertFalse(is_acceptable_value('tls_protocols_lol', - '!SSLv2, SSLv3, !SSLv3, ', None)) - self.assertTrue(is_acceptable_value('tls_protocols_lol', - '!SSLv2, !SSLv3', None)) - self.assertTrue(is_acceptable_value('tls_protocols_lol', - '!SSLv3, !TLSv1, !SSLv2', None)) - # TLSv1.2 is supported unambiguously - self.assertFalse(is_acceptable_value('tls_protocols_lol', - 'TLSv1, TLSv1.1,', None)) - self.assertFalse(is_acceptable_value('tls_protocols_lol', - 'TLSv1.2, !TLSv1.2,', None)) - self.assertTrue(is_acceptable_value('tls_protocols_lol', - 'TLSv1.2, ', None)) - self.assertTrue(is_acceptable_value('tls_protocols_lol', - 'TLSv1, TLSv1.1, TLSv1.2', None)) - -if __name__ == '__main__': # pragma: no cover - unittest.main() diff --git a/certbot-postfix/certbot_postfix/util.py b/certbot-postfix/certbot_postfix/util.py deleted file mode 100644 index 86a892140..000000000 --- a/certbot-postfix/certbot_postfix/util.py +++ /dev/null @@ -1,292 +0,0 @@ -"""Utility functions for use in the Postfix installer.""" -import logging -import re -import subprocess - -from certbot import errors -from certbot import util as certbot_util -from certbot.plugins import util as plugins_util -from certbot_postfix import constants - -logger = logging.getLogger(__name__) - -COMMAND = "postfix" - - -class PostfixUtilBase(object): - """A base class for wrapping Postfix command line utilities.""" - - def __init__(self, executable, config_dir=None): - """Sets up the Postfix utility class. - - :param str executable: name or path of the Postfix utility - :param str config_dir: path to an alternative Postfix config - - :raises .NoInstallationError: when the executable isn't found - - """ - self.executable = executable - verify_exe_exists(executable) - self._set_base_command(config_dir) - self.config_dir = None - - def _set_base_command(self, config_dir): - self._base_command = [self.executable] - if config_dir is not None: - self._base_command.extend(('-c', config_dir,)) - - def _call(self, extra_args=None): - """Runs the Postfix utility and returns the result. - - :param list extra_args: additional arguments for the command - - :returns: data written to stdout and stderr - :rtype: `tuple` of `str` - - :raises subprocess.CalledProcessError: if the command fails - - """ - args = list(self._base_command) - if extra_args is not None: - args.extend(extra_args) - return check_all_output(args) - - def _get_output(self, extra_args=None): - """Runs the Postfix utility and returns only stdout output. - - This function relies on self._call for running the utility. - - :param list extra_args: additional arguments for the command - - :returns: data written to stdout - :rtype: str - - :raises subprocess.CalledProcessError: if the command fails - - """ - return self._call(extra_args)[0] - -class PostfixUtil(PostfixUtilBase): - """Wrapper around Postfix CLI tool. - """ - - def __init__(self, config_dir=None): - super(PostfixUtil, self).__init__(COMMAND, config_dir) - - def test(self): - """Make sure the configuration is valid. - - :raises .MisconfigurationError: if the config is invalid - """ - try: - self._call(["check"]) - except subprocess.CalledProcessError as e: - logger.debug("Could not check postfix configuration:\n%s", e) - raise errors.MisconfigurationError( - "Postfix failed internal configuration check.") - - def restart(self): - """Restart or refresh the server content. - - :raises .PluginError: when server cannot be restarted - - """ - logger.info("Reloading Postfix configuration...") - if self._is_running(): - self._reload() - else: - self._start() - - - def _is_running(self): - """Is Postfix currently running? - - Uses the 'postfix status' command to determine if Postfix is - currently running using the specified configuration files. - - :returns: True if Postfix is running, otherwise, False - :rtype: bool - - """ - try: - self._call(["status"]) - except subprocess.CalledProcessError: - return False - return True - - def _start(self): - """Instructions Postfix to start running. - - :raises .PluginError: when Postfix cannot start - - """ - try: - self._call(["start"]) - except subprocess.CalledProcessError: - raise errors.PluginError("Postfix failed to start") - - def _reload(self): - """Instructs Postfix to reload its configuration. - - If Postfix isn't currently running, this method will fail. - - :raises .PluginError: when Postfix cannot reload - """ - try: - self._call(["reload"]) - except subprocess.CalledProcessError: - raise errors.PluginError( - "Postfix failed to reload its configuration") - -def check_all_output(*args, **kwargs): - """A version of subprocess.check_output that also captures stderr. - - This is the same as :func:`subprocess.check_output` except output - written to stderr is also captured and returned to the caller. The - return value is a tuple of two strings (rather than byte strings). - To accomplish this, the caller cannot set the stdout, stderr, or - universal_newlines parameters to :class:`subprocess.Popen`. - - Additionally, if the command exits with a nonzero status, output is - not included in the raised :class:`subprocess.CalledProcessError` - because Python 2.6 does not support this. Instead, the failure - including the output is logged. - - :param tuple args: positional arguments for Popen - :param dict kwargs: keyword arguments for Popen - - :returns: data written to stdout and stderr - :rtype: `tuple` of `str` - - :raises ValueError: if arguments are invalid - :raises subprocess.CalledProcessError: if the command fails - - """ - for keyword in ('stdout', 'stderr', 'universal_newlines',): - if keyword in kwargs: - raise ValueError( - keyword + ' argument not allowed, it will be overridden.') - - kwargs['stdout'] = subprocess.PIPE - kwargs['stderr'] = subprocess.PIPE - kwargs['universal_newlines'] = True - - process = subprocess.Popen(*args, **kwargs) - output, err = process.communicate() - retcode = process.poll() - if retcode: - cmd = kwargs.get('args') - if cmd is None: - cmd = args[0] - logger.debug( - "'%s' exited with %d. stdout output was:\n%s\nstderr output was:\n%s", - cmd, retcode, output, err) - raise subprocess.CalledProcessError(retcode, cmd) - return (output, err) - - -def verify_exe_exists(exe, message=None): - """Ensures an executable with the given name is available. - - If an executable isn't found for the given path or name, extra - directories are added to the user's PATH to help find system - utilities that may not be available in the default cron PATH. - - :param str exe: executable path or name - :param str message: Error message to print. - - :raises .NoInstallationError: when the executable isn't found - - """ - if message is None: - message = "Cannot find executable '{0}'.".format(exe) - if not (certbot_util.exe_exists(exe) or plugins_util.path_surgery(exe)): - raise errors.NoInstallationError(message) - -def report_master_overrides(name, overrides, acceptable_overrides=None): - """If the value for a parameter `name` is overridden by other services, - report a warning to notify the user. If `parameter` is a TLS version parameter - (i.e., `parameter` contains 'tls_protocols' or 'tls_mandatory_protocols'), then - `acceptable_overrides` isn't used each value in overrides is inspected for secure TLS - versions. - - :param str name: The name of the parameter that is being overridden. - :param list overrides: The values that other services are setting for `name`. - Each override is a tuple: (service name, value) - :param tuple acceptable_overrides: Override values that are acceptable. For instance, if - another service is overriding our parameter with a more secure option, we don't have - to warn. If this is set to None, errors are raised for *any* overrides of `name`! - """ - error_string = "" - for override in overrides: - service, value = override - # If this override is acceptable: - if acceptable_overrides is not None and \ - is_acceptable_value(name, value, acceptable_overrides): - continue - error_string += " {0}: {1}\n".format(service, value) - if error_string: - raise errors.PluginError("{0} is overridden with less secure options by the " - "following services in master.cf:\n".format(name) + error_string) - - -def is_acceptable_value(parameter, value, acceptable=None): - """ Returns whether the `value` for this `parameter` is acceptable, - given a tuple of `acceptable` values. If `parameter` is a TLS version parameter - (i.e., `parameter` contains 'tls_protocols' or 'tls_mandatory_protocols'), then - `acceptable` isn't used and `value` is inspected for secure TLS versions. - - :param str parameter: The name of the parameter being set. - :param str value: Proposed new value for parameter. - :param tuple acceptable: List of acceptable values for parameter. - """ - # Check if param value is a comma-separated list of protocols. - # Otherwise, just check whether the value is in the acceptable list. - if 'tls_protocols' in parameter or 'tls_mandatory_protocols' in parameter: - return _has_acceptable_tls_versions(value) - if acceptable is not None: - return value in acceptable - return False - - -def _has_acceptable_tls_versions(parameter_string): - """ - Checks to see if the list of TLS protocols is acceptable. - This requires that TLSv1.2 is supported, and neither SSLv2 nor SSLv3 are supported. - - Should be a string of protocol names delimited by commas, spaces, or colons. - - Postfix's documents suggest listing protocols to exclude, like "!SSLv2, !SSLv3". - Listing the protocols to include, like "TLSv1, TLSv1.1, TLSv1.2" is okay as well, - though not recommended - - When these two modes are interspersed, the presence of a single non-negated protocol name - (i.e. "TLSv1" rather than "!TLSv1") automatically excludes all other unnamed protocols. - - In addition, the presence of both a protocol name inclusion and exclusion isn't explicitly - documented, so this method should return False if it encounters contradicting statements - about TLSv1.2, SSLv2, or SSLv3. (for instance, "SSLv3, !SSLv3"). - """ - if not parameter_string: - return False - bad_versions = list(constants.TLS_VERSIONS) - for version in constants.ACCEPTABLE_TLS_VERSIONS: - del bad_versions[bad_versions.index(version)] - supported_version_list = re.split("[, :]+", parameter_string) - # The presence of any non-"!" protocol listing excludes the others by default. - inclusion_list = False - for version in supported_version_list: - if not version: - continue - if version in bad_versions: # short-circuit if we recognize any bad version - return False - if version[0] != "!": - inclusion_list = True - if inclusion_list: # For any inclusion list, we still require TLS 1.2. - if "TLSv1.2" not in supported_version_list or "!TLSv1.2" in supported_version_list: - return False - else: - for bad_version in bad_versions: - if "!" + bad_version not in supported_version_list: - return False - return True diff --git a/certbot-postfix/docs/.gitignore b/certbot-postfix/docs/.gitignore deleted file mode 100644 index ba65b13af..000000000 --- a/certbot-postfix/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/_build/ diff --git a/certbot-postfix/docs/Makefile b/certbot-postfix/docs/Makefile deleted file mode 100644 index 717ff654f..000000000 --- a/certbot-postfix/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -SPHINXPROJ = certbot-postfix -SOURCEDIR = . -BUILDDIR = _build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) \ No newline at end of file diff --git a/certbot-postfix/docs/api.rst b/certbot-postfix/docs/api.rst deleted file mode 100644 index 8668ec5d8..000000000 --- a/certbot-postfix/docs/api.rst +++ /dev/null @@ -1,8 +0,0 @@ -================= -API Documentation -================= - -.. toctree:: - :glob: - - api/** diff --git a/certbot-postfix/docs/api/installer.rst b/certbot-postfix/docs/api/installer.rst deleted file mode 100644 index 121d58d5b..000000000 --- a/certbot-postfix/docs/api/installer.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`certbot_postfix.installer` --------------------------------------- - -.. automodule:: certbot_postfix.installer - :members: diff --git a/certbot-postfix/docs/api/postconf.rst b/certbot-postfix/docs/api/postconf.rst deleted file mode 100644 index 917150e45..000000000 --- a/certbot-postfix/docs/api/postconf.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`certbot_postfix.postconf` -------------------------------- - -.. automodule:: certbot_postfix.postconf - :members: diff --git a/certbot-postfix/docs/conf.py b/certbot-postfix/docs/conf.py deleted file mode 100644 index a2f1a8092..000000000 --- a/certbot-postfix/docs/conf.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -from certbot.compat import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = u'certbot-postfix' -copyright = u'2018, Certbot Project' -author = u'Certbot Project' - -# The short X.Y version -version = u'0' -# The full version, including alpha/beta/rc tags -release = u'0' - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.viewcode', -] - -autodoc_member_order = 'bysource' -autodoc_default_flags = ['show-inheritance', 'private-members'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = u'en' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path . -exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store'] - -default_role = 'py:obj' - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# - -# http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs -# on_rtd is whether we are on readthedocs.org -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' -if not on_rtd: # only import and set the theme if we're building docs locally - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] -# otherwise, readthedocs.org uses their theme by default, so no need to specify it - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'certbot-postfixdoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'certbot-postfix.tex', u'certbot-postfix Documentation', - u'Certbot Project', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'certbot-postfix', u'certbot-postfix Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'certbot-postfix', u'certbot-postfix Documentation', - author, 'certbot-postfix', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Extension configuration ------------------------------------------------- - -# -- Options for intersphinx extension --------------------------------------- - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = { - 'python': ('https://docs.python.org/', None), - 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://certbot.eff.org/docs/', None), -} - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True diff --git a/certbot-postfix/docs/index.rst b/certbot-postfix/docs/index.rst deleted file mode 100644 index 3d6697bcb..000000000 --- a/certbot-postfix/docs/index.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. certbot-postfix documentation master file, created by - sphinx-quickstart on Wed May 2 16:01:06 2018. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to certbot-postfix's documentation! -=========================================== - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - -.. automodule:: certbot_postfix - :members: - -.. toctree:: - :maxdepth: 1 - - api - - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/certbot-postfix/docs/make.bat b/certbot-postfix/docs/make.bat deleted file mode 100644 index 23fbdc93c..000000000 --- a/certbot-postfix/docs/make.bat +++ /dev/null @@ -1,36 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build -set SPHINXPROJ=certbot-postfix - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% - -:end -popd diff --git a/certbot-postfix/local-oldest-requirements.txt b/certbot-postfix/local-oldest-requirements.txt deleted file mode 100644 index da509406e..000000000 --- a/certbot-postfix/local-oldest-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -# Remember to update setup.py to match the package versions below. -acme[dev]==0.29.0 --e .[dev] diff --git a/certbot-postfix/setup.cfg b/certbot-postfix/setup.cfg deleted file mode 100644 index 2a9acf13d..000000000 --- a/certbot-postfix/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal = 1 diff --git a/certbot-postfix/setup.py b/certbot-postfix/setup.py deleted file mode 100644 index e708863b2..000000000 --- a/certbot-postfix/setup.py +++ /dev/null @@ -1,64 +0,0 @@ -from setuptools import setup -from setuptools import find_packages - - -version = '0.26.0.dev0' - -install_requires = [ - 'acme>=0.29.0', - 'certbot>=0.34.0.dev0', - 'setuptools', - 'six', - 'zope.component', - 'zope.interface', -] - -docs_extras = [ - 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags - 'sphinx_rtd_theme', -] - -setup( - name='certbot-postfix', - version=version, - description="Postfix plugin for Certbot", - url='https://github.com/certbot/certbot', - author="Certbot Project", - author_email='client-dev@letsencrypt.org', - license='Apache License 2.0', - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Environment :: Plugins', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Topic :: Communications :: Email :: Mail Transport Agents', - 'Topic :: Security', - 'Topic :: System :: Installation/Setup', - 'Topic :: System :: Networking', - 'Topic :: System :: Systems Administration', - 'Topic :: Utilities', - ], - - packages=find_packages(), - include_package_data=True, - install_requires=install_requires, - extras_require={ - 'docs': docs_extras, - }, - entry_points={ - 'certbot.plugins': [ - 'postfix = certbot_postfix:Installer', - ], - }, - test_suite='certbot_postfix', -) diff --git a/certbot/plugins/disco.py b/certbot/plugins/disco.py index 4b8e9f7ab..ec2bff8b7 100644 --- a/certbot/plugins/disco.py +++ b/certbot/plugins/disco.py @@ -39,7 +39,6 @@ class PluginEntryPoint(object): "certbot-dns-route53", "certbot-dns-sakuracloud", "certbot-nginx", - "certbot-postfix", ] """Distributions for which prefix will be omitted.""" diff --git a/docs/using.rst b/docs/using.rst index d398c1c4d..8e93bcb41 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -280,7 +280,6 @@ external_ Y N A plugin for convenient scripting (See also ticket 2782_) icecast_ N Y Deploy certificates to Icecast 2 streaming media servers pritunl_ N Y Install certificates in pritunl distributed OpenVPN servers proxmox_ N Y Install certificates in Proxmox Virtualization servers -postfix_ N Y STARTTLS Everywhere is becoming a Certbot Postfix/Exim plugin heroku_ Y Y Integration with Heroku SSL =========== ==== ==== =============================================================== @@ -294,7 +293,6 @@ heroku_ Y Y Integration with Heroku SSL .. _pritunl: https://github.com/kharkevich/letsencrypt-pritunl .. _proxmox: https://github.com/kharkevich/letsencrypt-proxmox .. _external: https://github.com/marcan/letsencrypt-external -.. _postfix: https://github.com/EFForg/starttls-everywhere .. _heroku: https://github.com/gboudreau/certbot-heroku If you're interested, you can also :ref:`write your own plugin `. diff --git a/tools/_venv_common.py b/tools/_venv_common.py index 1594fd05f..ac3b5f98d 100644 --- a/tools/_venv_common.py +++ b/tools/_venv_common.py @@ -39,7 +39,6 @@ REQUIREMENTS = [ '-e certbot-dns-route53', '-e certbot-dns-sakuracloud', '-e certbot-nginx', - '-e certbot-postfix', '-e letshelp-certbot', '-e certbot-compatibility-test', '-e certbot-ci', diff --git a/tools/install_and_test.py b/tools/install_and_test.py index 288226527..6987cf2b1 100755 --- a/tools/install_and_test.py +++ b/tools/install_and_test.py @@ -14,8 +14,7 @@ import shutil import subprocess import re -SKIP_PROJECTS_ON_WINDOWS = [ - 'certbot-apache', 'certbot-postfix', 'letshelp-certbot'] +SKIP_PROJECTS_ON_WINDOWS = ['certbot-apache', 'letshelp-certbot'] def call_with_print(command, cwd=None): diff --git a/tox.cover.py b/tox.cover.py index d0f97626a..65dc4a8a9 100755 --- a/tox.cover.py +++ b/tox.cover.py @@ -9,7 +9,7 @@ DEFAULT_PACKAGES = [ 'certbot_dns_digitalocean', 'certbot_dns_dnsimple', 'certbot_dns_dnsmadeeasy', 'certbot_dns_gehirn', 'certbot_dns_google', 'certbot_dns_linode', 'certbot_dns_luadns', 'certbot_dns_nsone', 'certbot_dns_ovh', 'certbot_dns_rfc2136', 'certbot_dns_route53', - 'certbot_dns_sakuracloud', 'certbot_nginx', 'certbot_postfix', 'letshelp_certbot'] + 'certbot_dns_sakuracloud', 'certbot_nginx', 'letshelp_certbot'] COVER_THRESHOLDS = { 'certbot': {'linux': 98, 'windows': 93}, @@ -30,12 +30,10 @@ COVER_THRESHOLDS = { 'certbot_dns_route53': {'linux': 92, 'windows': 92}, 'certbot_dns_sakuracloud': {'linux': 97, 'windows': 97}, 'certbot_nginx': {'linux': 97, 'windows': 97}, - 'certbot_postfix': {'linux': 100, 'windows': 100}, 'letshelp_certbot': {'linux': 100, 'windows': 100} } -SKIP_PROJECTS_ON_WINDOWS = [ - 'certbot-apache', 'certbot-postfix', 'letshelp-certbot'] +SKIP_PROJECTS_ON_WINDOWS = ['certbot-apache', 'letshelp-certbot'] def cover(package): diff --git a/tox.ini b/tox.ini index 52ae1e97d..fed30976f 100644 --- a/tox.ini +++ b/tox.ini @@ -35,7 +35,6 @@ all_packages = certbot-apache \ {[base]dns_packages} \ certbot-nginx \ - certbot-postfix \ letshelp-certbot install_packages = python {toxinidir}/tools/pip_install_editable.py {[base]all_packages} @@ -59,7 +58,6 @@ source_paths = certbot-dns-route53/certbot_dns_route53 certbot-dns-sakuracloud/certbot_dns_sakuracloud certbot-nginx/certbot_nginx - certbot-postfix/certbot_postfix letshelp-certbot/letshelp_certbot tests/lock_test.py @@ -111,12 +109,6 @@ commands = setenv = {[testenv:py27-oldest]setenv} -[testenv:py27-postfix-oldest] -commands = - {[base]install_and_test} certbot-postfix -setenv = - {[testenv:py27-oldest]setenv} - [testenv:py27-cover] basepython = python2.7 commands =