Compare commits

...

3 Commits

Author SHA1 Message Date
sydneyli
861ff0c910 fixes 2018-10-25 08:50:32 -07:00
sydneyli
643a5bb48f version bump 2018-10-25 07:15:37 -07:00
sydneyli
6f51198394 Sendmail mvp 2018-10-25 06:27:56 -07:00
15 changed files with 706 additions and 0 deletions

View File

@@ -0,0 +1,216 @@
Copyright 2018 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.
Incorporating code from nginxparser
Copyright 2014 Fatih Erikli
Licensed MIT
Text of Apache 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
Text of MIT License
===================
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,2 @@
include LICENSE.txt
include README.rst

View File

@@ -0,0 +1 @@
Sendmail plugin for Certbot

View File

@@ -0,0 +1 @@
"""Certbot sendmail plugin."""

View File

@@ -0,0 +1,255 @@
"""Sendmail Configuration"""
import sys
import logging
import os
import difflib
import re
import socket
import subprocess
import tempfile
import time
import OpenSSL
import six
import zope.component
import zope.interface
from certbot import constants as core_constants
from certbot import crypto_util
from certbot import errors
from certbot import interfaces
from certbot import util
from certbot.plugins import common
from certbot_sendmail import constants
logger = logging.getLogger(__name__)
class FileChanges(object):
def __init__(self, filepath):
self._filepath = filepath
self._lines = []
self._append = []
if not os.path.isfile(filepath):
open(filepath, "a").close()
with open(filepath) as f:
self._lines = f.readlines()
self._diff = {} # line number: new line
def replace_first(self, regex_find_line, replace_value, full_replace):
""" regex to match line, new line to replace it with."""
p = re.compile(regex_find_line)
for i, line in enumerate(self._lines):
result = re.search(p, line)
if not result or not result.group("value"):
continue
if result.group("value") == replace_value:
return # already set correctly
start, end = result.span("value")
self._diff[i] = line[0:start] + replace_value + line[end:]
return
self._append.append(full_replace)
def flush(self, diff_file=None):
""" print diff """
new_filelines = []
for i, line in enumerate(self._lines):
to_append = line
if i in self._diff:
to_append = self._diff[i]
new_filelines.append(to_append)
new_filelines.extend(self._append)
diff = "".join(difflib.unified_diff(self._lines, new_filelines))
pause = True
if len(self._append) == 0 and len(self._diff) == 0:
message = "Your config files are already up-to-date!\n"
pause = False
elif diff_file:
with open(diff_file, "w") as f:
f.write(diff)
message = ("The appropriate diff has been written to {diff_file}.\n"
"Review these changes, then apply them with:\n\n"
" patch -b {tls_file} -i {diff_file}\n\n"
"This should also create a backup of the original file at {tls_file}.orig\n").format(
diff_file=diff_file, tls_file=self._filepath)
else:
message = ("Review and apply the following diff to {tls_file}.\n"
"Continue when finished:\n\n{content}\n\n".format(
tls_file=self._filepath, content=diff))
zope.component.getUtility(interfaces.IDisplay).notification(message, pause=True)
@zope.interface.implementer(interfaces.IInstaller)
@zope.interface.provider(interfaces.IPluginFactory)
class SendmailConfigurator(common.Installer):
# pylint: disable=too-many-instance-attributes,too-many-public-methods
"""Sendmail configurator.
.. todo:: Add proper support for comments in the config. Currently,
config files modified by the configurator will lose all their comments.
:ivar config: Configuration.
:type config: :class:`~certbot.interfaces.IConfig`
:ivar str save_notes: Human-readable config change notes
:ivar reverter: saves and reverts checkpoints
:type reverter: :class:`certbot.reverter.Reverter`
"""
description = "Sendmail Web Server plugin - Alpha"
@classmethod
def add_parser_arguments(cls, add):
add("server-root", default=constants.CLI_DEFAULTS["server_root"],
help="Sendmail server root directory.")
add("tls-config-file", default=constants.CLI_DEFAULTS["tls_config_file"],
help="Filename for the relevant TLS options.")
add("diff-file", default=constants.CLI_DEFAULTS["diff_file"],
help="Optional output file for diff. If specified diff will be written"
"to file rather than stdout.")
@property
def tls_config_file(self):
"""Sendmail TLS config file."""
# TODO (sydli): check if there's an intermediate "TLS" directory; if so, drop
# it there.
return os.path.join(self.conf("server_root"), self.conf("tls-config-file"))
def __init__(self, *args, **kwargs):
"""Initialize an Sendmail Configurator. """
super(SendmailConfigurator, self).__init__(*args, **kwargs)
# Files to save
self.save_notes = ""
self.reverter.recovery_routine()
self.changes = None
self._keypath = None
# This is called in determine_authenticator and determine_installer
def prepare(self):
"""Prepare the authenticator/installer.
:raises .errors.NoInstallationError: If Sendmail ctl cannot be found
:raises .errors.MisconfigurationError: If Sendmail is misconfigured
"""
# Prevent two Sendmail plugins from modifying a config at once
try:
util.lock_dir_until_exit(self.conf("server-root"))
except (OSError, errors.LockError):
logger.debug("Encountered error:", exc_info=True)
raise errors.PluginError(
"Unable to lock %s", self.conf("server-root"))
self.changes = FileChanges(self.tls_config_file)
# Entry point in main.py for installing cert
def deploy_cert(self, domain, cert_path, key_path,
chain_path=None, fullchain_path=None):
# pylint: disable=unused-argument
"""Deploys certificate to specified virtual host.
"""
fullchain_dir, _ = os.path.split(fullchain_path)
regex_metapattern = r"define\(`conf{param}',\s*`(?P<value>.+)'\)"
full_string = "define(`conf{param}', `{value}')dnl\n"
# TODO: Instead of setting CACERT_PATH to the live/ dir,
# We should really just ensure that Let's Encrypt's cert
# is trusted.
config_params = {
"CACERT_PATH": fullchain_dir,
"CACERT": fullchain_path,
"SERVER_CERT": cert_path,
"SERVER_KEY": key_path,
"CLIENT_CERT": cert_path,
"CLIENT_KEY": key_path,
}
for param in config_params:
yay_for_regex_parsing = regex_metapattern.format(param=param)
self.changes.replace_first(yay_for_regex_parsing,
config_params[param],
full_string.format(param=param, value=config_params[param]))
self._keypath = key_path
##################################
# enhancement methods (IInstaller)
##################################
def supported_enhancements(self): # pylint: disable=no-self-use
"""Returns currently supported enhancements."""
return []
def enhance(self, domain, enhancement, options=None):
"""Enhance configuration.
:param str domain: domain to enhance
:param str enhancement: enhancement type defined in
:const:`~certbot.constants.ENHANCEMENTS`
:param options: options for the enhancement
See :const:`~certbot.constants.ENHANCEMENTS`
documentation for appropriate parameter.
"""
raise NotImplemented("The Sendmail plugin does not support any enhancements")
######################################
# Sendmail management (IInstaller)
######################################
def restart(self):
"""Restarts sendmail. Not implemented. """
if self._keypath:
os.chmod(self._keypath, 0o640)
try:
proc = subprocess.Popen(["make", "-C", self.conf("server_root")])
out, err = proc.communicate()
proc = subprocess.Popen(["make", "install", "-C", self.conf("server_root")])
out, err = proc.communicate()
proc = subprocess.Popen(["service", "sendmail", "restart"])
out, err = proc.communicate()
except (OSError, ValueError):
raise errors.MisconfigurationError("nginx restart failed")
def config_test(self): # pylint: disable=no-self-use
"""Check the configuration of Sendmail for errors.
"""
pass
def more_info(self):
"""Human-readable string to help understand the module"""
return (
"Configures Sendmail to install STARTTLS with a valid cert.{0}"
"Server root: {root}{0}".format(
os.linesep, root=self.parser.config_root))
###################################################
# Wrapper functions for Reverter class (IInstaller)
###################################################
def save(self, title=None, temporary=False):
"""Saves all changes to the 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.
:param bool temporary: Indicates whether the changes made will
be quickly reversed in the future (ie. challenges)
:raises .errors.PluginError: If there was an error in
an attempt to save the configuration, or an error creating a
checkpoint
"""
self.changes.flush(self.conf("diff_file"))
def rollback_checkpoints(self, rollback=1):
pass
def recovery_routine(self):
pass
def get_all_names(self):
"""Returns all names that may be authenticated.
"""
return []

View File

@@ -0,0 +1,9 @@
"""sendmail plugin constants."""
import pkg_resources
CLI_DEFAULTS = dict(
server_root="/etc/mail",
tls_config_file="starttls.m4",
diff_file=None,
)
"""CLI defaults."""

View File

@@ -0,0 +1,11 @@
# This file contains important security parameters. If you modify this file
# manually, Certbot will be unable to automatically provide future security
# updates. Instead, Certbot will print and log an error message with a path to
# the up-to-date file that you will need to refer to when manually updating
# this file.
ssl_protocols = !SSLv3 !SSLv2
ssl_prefer_server_ciphers = yes
ssl_cipher_list = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS"
ssl_dh_parameters_length = 2048

View File

@@ -0,0 +1 @@
"""Certbot Sendmail Tests"""

View File

@@ -0,0 +1,80 @@
"""Test for certbot_sendmail.configurator."""
import unittest
import mock
import os
import subprocess
import shutil
import tempfile
from certbot import errors
from certbot.tests import util as certbot_test_util
from certbot.plugins import common
from certbot_sendmail import configurator
class SendmailConfiguratorTest(unittest.TestCase):
"""Test a semi complex vhost configuration."""
def setUp(self):
super(SendmailConfiguratorTest, self).setUp()
def test_prepare(self):
config, _ = get_configurator()
config.prepare()
@certbot_test_util.patch_get_utility()
def test_deploy(self, mock_getutility):
mock_utility = mock_getutility()
mock_utility.notification = mock.MagicMock(return_value=True)
config, _ = get_configurator()
config.prepare()
config.deploy_cert("example.com", "path/cert123.pem", "path/ultrasecret123.pem",
"path/one_chain.pem", "path/full_chain.pem")
config.save()
@certbot_test_util.patch_get_utility()
def test_configure_empty(self, mock_getutility):
mock_utility = mock_getutility()
mock_utility.notification = mock.MagicMock(return_value=True)
config, tempdir = get_configurator(tls_filename="nonexistant.m4", output_diff="output.diff")
config.prepare()
config.deploy_cert("example.com", "path/cert123.pem", "path/ultrasecret123.pem",
"path/one_chain.pem", "path/full_chain.pem")
config.save()
with open(os.path.join(tempdir, "output.diff")) as f:
actual = f.readlines()
expected = [
"--- \n", "+++ \n", "@@ -0,0 +1,6 @@\n",
"+define(`confSERVER_CERT', `path/cert123.pem')dnl\n",
"+define(`confCACERT_PATH', `path')dnl\n",
"+define(`confSERVER_KEY', `path/ultrasecret123.pem')dnl\n",
"+define(`confCACERT', `path/full_chain.pem')dnl\n",
"+define(`confCLIENT_CERT', `path/cert123.pem')dnl\n",
"+define(`confCLIENT_KEY', `path/ultrasecret123.pem')dnl\n",
]
self.assertEqual(tuple(expected), tuple(actual))
def get_configurator(tls_filename="starttls.m4", write_tls_data=None, output_diff=None):
tempdir, _, _ = common.dir_setup(
"etc_mail", "certbot_sendmail.tests")
backups = os.path.join(tempdir, "backups")
config_path = os.path.join(tempdir, "etc_mail")
if write_tls_data:
tls_path = os.path.join(config_path, tls_filename)
with open(tls_path, 'w') as f:
f.write(write_tls_data)
diff_file = None
if output_diff:
diff_file = os.path.join(tempdir, output_diff)
return configurator.SendmailConfigurator(
config=mock.MagicMock(
backup_dir=backups,
temp_checkpoint_dir=os.path.join(backups, "temp_checkpoints"),
in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
sendmail_server_root=config_path,
sendmail_tls_config_file=tls_filename,
sendmail_diff_file=diff_file,
), name="sendmail"), tempdir
if __name__ == "__main__":
unittest.main() # pragma: no cover

View File

@@ -0,0 +1,55 @@
divert(-1)dnl
####################################################################
####################################################################
divert(0)dnl
VERSIONID(`$Id: starttls.m4,v 8.15.2-10 2018-01-13 23:43:05 cowboy Exp $')
dnl #
dnl #---------------------------------------------------------------------
dnl # Bring in Autoconf results
dnl #---------------------------------------------------------------------
ifdef(`sm_version', `dnl',
`include(`/usr/share/sendmail/cf/debian/autoconf.m4')dnl')
dnl #
dnl # Check to see if inclusion is valid (version >= 8.11.0, tls enabled)
ifelse(eval(sm_version_math >= 527104), `1', `dnl
ifelse(sm_enable_tls, `yes', `dnl
dnl #
dnl # To support shared keyfiles, we need them to be group readable
dnl #
define(`confDONT_BLAME_SENDMAIL',dnl
defn(`confDONT_BLAME_SENDMAIL')`,GroupReadableKeyFile')dnl
dnl #
dnl # ...Do not touch anything above this line...
dnl #
dnl # Set a more reasonable timeout on negotiation
dnl #
define(`confTO_STARTTLS', `2m')dnl # <= EDIT
dnl #
dnl # CA directory - CA certs should be herein
define(`confCACERT_PATH', `/etc/ssl/certs')dnl # <= EDIT
dnl #
dnl # CA file (may be the same as client/server certificate)
define(`confCACERT', `/etc/mail/tls/sendmail-server.crt')dnl # <= EDIT
dnl #
dnl # Certificate Revocation List
define(`confCRL', `')dnl # <= EDIT
dnl # CRL not found... do not issue warnings on it!
undefine(`confCRL')dnl
dnl #
dnl # Server certificate/key (can be in the same file, and shared w/client)
dnl # NOTE: The key must *NOT* be encrypted !!!
define(`confSERVER_CERT', `/etc/mail/tls/sendmail-server.crt')dnl # <= EDIT
define(`confSERVER_KEY', `/etc/mail/tls/sendmail-common.key')dnl # <= EDIT
dnl #
dnl # Client certificate/key (can be in the same file, and shared w/server)
dnl # NOTE: The key must *NOT* be encrypted !!!
define(`confCLIENT_CERT', `/etc/mail/tls/sendmail-client.crt')dnl # <= EDIT
define(`confCLIENT_KEY', `/etc/mail/tls/sendmail-common.key')dnl # <= EDIT
dnl #
dnl # DH parameters
define(`confDH_PARAMETERS', `/etc/mail/tls/sendmail-common.prm')dnl # <= EDIT
dnl #
dnl # Optional settings
define(`confTLS_SRV_OPTIONS', `V')dnl # <= EDIT
dnl #
')')dnl

View File

@@ -0,0 +1,2 @@
[bdist_wheel]
universal = 1

70
certbot-sendmail/setup.py Normal file
View File

@@ -0,0 +1,70 @@
import sys
from setuptools import setup
from setuptools import find_packages
version = '0.28.0.dev0'
# Please update tox.ini when modifying dependency version requirements
install_requires = [
'acme=={0}'.format(version),
'certbot=={0}'.format(version),
'mock',
'PyOpenSSL',
'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary?
# For pkg_resources. >=1.0 so pip resolves it to a version cryptography
# will tolerate; see #2599:
'setuptools>=1.0',
'zope.interface',
]
docs_extras = [
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
'sphinx_rtd_theme',
]
setup(
name='certbot-sendmail',
version=version,
description="Sendmail plugin for Certbot",
url='https://github.com/letsencrypt/letsencrypt',
author="Certbot Project",
author_email='client-dev@letsencrypt.org',
license='Apache License 2.0',
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.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP',
'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': [
'sendmail = certbot_sendmail.configurator:SendmailConfigurator',
],
},
test_suite='certbot_sendmail',
)

View File

@@ -41,6 +41,7 @@ class PluginEntryPoint(object):
"certbot-dns-sakuracloud",
"certbot-nginx",
"certbot-postfix",
"certbot-sendmail",
]
"""Distributions for which prefix will be omitted."""

View File

@@ -30,5 +30,6 @@ fi
-e certbot-dns-sakuracloud \
-e certbot-nginx \
-e certbot-postfix \
-e certbot-sendmail \
-e letshelp-certbot \
-e certbot-compatibility-test

View File

@@ -29,5 +29,6 @@ fi
-e certbot-dns-sakuracloud \
-e certbot-nginx \
-e certbot-postfix \
-e certbot-sendmail \
-e letshelp-certbot \
-e certbot-compatibility-test