From 1802b87a1244eb134ae87292564d6064ad3700d4 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 16:01:52 +0000 Subject: [PATCH 001/216] Revert "Remove Python 2.6 support." This reverts commit 31d37a395393d74047964c6111300a09a446e4de. --- .travis.yml | 1 + setup.py | 1 + tox.ini | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 46b14fe63..9f750e616 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ env: - GOPATH=/tmp/go - PATH=$GOPATH/bin:$PATH matrix: + - TOXENV=py26 BOULDER_INTEGRATION=1 - TOXENV=py27 BOULDER_INTEGRATION=1 - TOXENV=lint - TOXENV=cover diff --git a/setup.py b/setup.py index 016dc146e..9fb58752a 100644 --- a/setup.py +++ b/setup.py @@ -98,6 +98,7 @@ setup( 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', diff --git a/tox.ini b/tox.ini index b10558077..83a3d07ec 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ # acme and letsencrypt are not yet on pypi, so when Tox invokes # "install *.zip", it will not find deps skipsdist = true -envlist = py27,cover,lint +envlist = py26,py27,py33,py34,cover,lint [testenv] commands = @@ -40,6 +40,7 @@ commands = ./tox.cover.sh [testenv:lint] +# recent versions of pylint do not support Python 2.6 (#97, #187) basepython = python2.7 # separating into multiple invocations disables cross package # duplicate code checking; if one of the commands fails, others will From a1a6120abfa5e5690f9cee615b069186be5205ca Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 16:03:32 +0000 Subject: [PATCH 002/216] Add py3 trove classifiers for acme --- acme/setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/acme/setup.py b/acme/setup.py index 36a724f97..ee55d6b7b 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -53,6 +53,9 @@ setup( 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', ], From 3bc2b30e933ee758d8c3de35fe42f90998a33c53 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 16:17:08 +0000 Subject: [PATCH 003/216] StreamHandler py2.6 fix --- letsencrypt/colored_logging.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt/colored_logging.py b/letsencrypt/colored_logging.py index 170da0b38..50d2146ac 100644 --- a/letsencrypt/colored_logging.py +++ b/letsencrypt/colored_logging.py @@ -17,7 +17,10 @@ class StreamHandler(logging.StreamHandler): """ def __init__(self, stream=None): - super(StreamHandler, self).__init__(stream) + if sys.version_info < (2, 7): + StreamHandler.__init__(self) # pragma: no cover + else: + super(StreamHandler, self).__init__(stream) self.colored = (sys.stderr.isatty() if stream is None else stream.isatty()) self.red_level = logging.WARNING From 6b53f2f3a76e8264088617ddbae6f614427af724 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 16:48:24 +0000 Subject: [PATCH 004/216] Drop recursive loop (logging.StreamHandler) --- letsencrypt/colored_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/colored_logging.py b/letsencrypt/colored_logging.py index 50d2146ac..53234510d 100644 --- a/letsencrypt/colored_logging.py +++ b/letsencrypt/colored_logging.py @@ -18,7 +18,7 @@ class StreamHandler(logging.StreamHandler): def __init__(self, stream=None): if sys.version_info < (2, 7): - StreamHandler.__init__(self) # pragma: no cover + logging.StreamHandler.__init__(self) # pragma: no cover else: super(StreamHandler, self).__init__(stream) self.colored = (sys.stderr.isatty() if stream is None else From 9702495ff0d3497e3e31358dc8b686349bfdab8d Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 16:49:49 +0000 Subject: [PATCH 005/216] lint: locally disable non-parent-init-called --- letsencrypt/colored_logging.py | 1 + 1 file changed, 1 insertion(+) diff --git a/letsencrypt/colored_logging.py b/letsencrypt/colored_logging.py index 53234510d..fd4a2b2fe 100644 --- a/letsencrypt/colored_logging.py +++ b/letsencrypt/colored_logging.py @@ -18,6 +18,7 @@ class StreamHandler(logging.StreamHandler): def __init__(self, stream=None): if sys.version_info < (2, 7): + # pylint: disable=non-parent-init-called logging.StreamHandler.__init__(self) # pragma: no cover else: super(StreamHandler, self).__init__(stream) From 9427c474ab7010fc7d8bd07a91ea208cddb8ffb1 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 16:55:59 +0000 Subject: [PATCH 006/216] More py2.6 love for StreamHandler. --- letsencrypt/colored_logging.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/letsencrypt/colored_logging.py b/letsencrypt/colored_logging.py index fd4a2b2fe..c47ad2707 100644 --- a/letsencrypt/colored_logging.py +++ b/letsencrypt/colored_logging.py @@ -35,10 +35,9 @@ class StreamHandler(logging.StreamHandler): :rtype: str """ - output = super(StreamHandler, self).format(record) + out = (StreamHandler.format(self, record) if sys.version_info < (2, 7) + else super(StreamHandler, self).format(record)) if self.colored and record.levelno >= self.red_level: - return ''.join((le_util.ANSI_SGR_RED, - output, - le_util.ANSI_SGR_RESET)) + return ''.join((le_util.ANSI_SGR_RED, out, le_util.ANSI_SGR_RESET)) else: - return output + return out From 8788f956a7fe3a9266357c79e8ca89b90c43cef8 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 17:10:05 +0000 Subject: [PATCH 007/216] Again, drop recursive loop for StreamHandler py26 --- letsencrypt/colored_logging.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/colored_logging.py b/letsencrypt/colored_logging.py index c47ad2707..28d0bea3c 100644 --- a/letsencrypt/colored_logging.py +++ b/letsencrypt/colored_logging.py @@ -35,7 +35,8 @@ class StreamHandler(logging.StreamHandler): :rtype: str """ - out = (StreamHandler.format(self, record) if sys.version_info < (2, 7) + out = (logging.StreamHandler.format(self, record) + if sys.version_info < (2, 7) else super(StreamHandler, self).format(record)) if self.colored and record.levelno >= self.red_level: return ''.join((le_util.ANSI_SGR_RED, out, le_util.ANSI_SGR_RESET)) From 5ab54c5bd11c3fe5e174e66f29e2d4312d0839c9 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 17:18:04 +0000 Subject: [PATCH 008/216] Fix assertRaises for py2.6 --- letsencrypt/tests/crypto_util_test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/tests/crypto_util_test.py b/letsencrypt/tests/crypto_util_test.py index 2e04c748a..d1790349e 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/letsencrypt/tests/crypto_util_test.py @@ -227,9 +227,8 @@ class CertLoaderTest(unittest.TestCase): def test_load_invalid_cert(self): from letsencrypt.crypto_util import pyopenssl_load_certificate bad_cert_data = CERT.replace("BEGIN CERTIFICATE", "ASDFASDFASDF!!!") - - with self.assertRaises(errors.Error): - pyopenssl_load_certificate(bad_cert_data) + self.assertRaises( + errors.Error, pyopenssl_load_certificate, bad_cert_data) if __name__ == '__main__': From 4c2d5dbdfc03e5ce2b8c2fd0ed77c4387cb6b60b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 11 Oct 2015 17:23:23 +0000 Subject: [PATCH 009/216] Pass stream to StreamHandler.__init__ --- letsencrypt/colored_logging.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/colored_logging.py b/letsencrypt/colored_logging.py index 28d0bea3c..443364ddd 100644 --- a/letsencrypt/colored_logging.py +++ b/letsencrypt/colored_logging.py @@ -18,8 +18,9 @@ class StreamHandler(logging.StreamHandler): def __init__(self, stream=None): if sys.version_info < (2, 7): + # pragma: no cover # pylint: disable=non-parent-init-called - logging.StreamHandler.__init__(self) # pragma: no cover + logging.StreamHandler.__init__(self, stream) else: super(StreamHandler, self).__init__(stream) self.colored = (sys.stderr.isatty() if stream is None else From 3ee4ee0996bf55e09913d6ca36cb66b2fd868528 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Oct 2015 18:07:21 -0700 Subject: [PATCH 010/216] Added lockfiles around link update --- letsencrypt/storage.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 8a0f4829e..354ea6b7c 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -378,9 +378,20 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes :param int version: the desired version""" + lockfiles = [] + for kind in ALL_FOUR: + lockfile = os.path.join(os.path.dirname(getattr(self, kind)), + "{0}.lock".format(kind)) + with open(lockfile, "w") as f: + f.write("{0}\n".format(self.current_target(kind))) + lockfiles.append(lockfile) + for kind in ALL_FOUR: self._update_link_to(kind, version) + for lockfile in lockfiles: + os.unlink(lockfile) + def _notafterbefore(self, method, version): """Internal helper function for finding notbefore/notafter.""" if version is None: From 15cf1c1a4e226146e560197e0ba85d368de6fbab Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Oct 2015 18:23:12 -0700 Subject: [PATCH 011/216] Added _lockfiles --- letsencrypt/storage.py | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 354ea6b7c..69ff272b3 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -205,6 +205,21 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # happen as a result of random tampering by a sysadmin, or # filesystem errors, or crashes.) + def _lockfiles(self): + """Returns the path of all lockfiles used by this lineage. + + :returns: the path of all lockfiles used by this lineage + :rtype: list + + """ + lockfiles = [] + for kind in ALL_FOUR: + lock_dir = os.path.dirname(getattr(self, kind)) + lock_base = "{0}.lock".format(kind) + lockfiles.append(os.path.join(lock_dir, lock_base)) + + return lockfiles + def current_target(self, kind): """Returns full path to which the specified item currently points. @@ -376,15 +391,14 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes def update_all_links_to(self, version): """Change all member objects to point to the specified version. - :param int version: the desired version""" + :param int version: the desired version - lockfiles = [] - for kind in ALL_FOUR: - lockfile = os.path.join(os.path.dirname(getattr(self, kind)), - "{0}.lock".format(kind)) + """ + lockfiles = self._lockfiles() + for lockfile in lockfiles: + kind = os.path.splitext(os.path.basename(lockfile))[0] with open(lockfile, "w") as f: f.write("{0}\n".format(self.current_target(kind))) - lockfiles.append(lockfile) for kind in ALL_FOUR: self._update_link_to(kind, version) From 49ba0c05e745e2d07fd12f243c11c19112299f12 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Oct 2015 18:35:07 -0700 Subject: [PATCH 012/216] Added recovery function --- letsencrypt/storage.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 69ff272b3..4047143a9 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -206,9 +206,9 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # filesystem errors, or crashes.) def _lockfiles(self): - """Returns the path of all lockfiles used by this lineage. + """Returns the kind and path of all used lockfiles. - :returns: the path of all lockfiles used by this lineage + :returns: list of (kind, lockfile) tuples :rtype: list """ @@ -216,10 +216,30 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes for kind in ALL_FOUR: lock_dir = os.path.dirname(getattr(self, kind)) lock_base = "{0}.lock".format(kind) - lockfiles.append(os.path.join(lock_dir, lock_base)) + lockfiles.append((kind, os.path.join(lock_dir, lock_base))) return lockfiles + def _fix_symlinks(self): + """Fixes symlinks in the event of an incomplete version update. + + If there is no problem with the current symlinks, this function + has no effect. + + """ + lockfiles = self._lockfiles() + if all(os.path.exists(lockfile) for lockfile in lockfiles): + for kind, lockfile in lockfiles: + link = getattr(self, kind) + if os.path.lexists(link): + os.unlink(link) + with open(lockfile) as f: + os.symlink(f.readline().rstrip(), link) + else: + for _, lockfile in lockfiles: + if os.path.exists(lockfile): + os.unlink(lockfile) + def current_target(self, kind): """Returns full path to which the specified item currently points. @@ -395,15 +415,14 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes """ lockfiles = self._lockfiles() - for lockfile in lockfiles: - kind = os.path.splitext(os.path.basename(lockfile))[0] + for kind, lockfile in lockfiles: with open(lockfile, "w") as f: f.write("{0}\n".format(self.current_target(kind))) for kind in ALL_FOUR: self._update_link_to(kind, version) - for lockfile in lockfiles: + for _, lockfile in lockfiles: os.unlink(lockfile) def _notafterbefore(self, method, version): From f8f80f5392badbb71cb5e1fe8c6b0da6f64833ed Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Oct 2015 18:39:17 -0700 Subject: [PATCH 013/216] Added calls to recovery code --- letsencrypt/storage.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 4047143a9..7d8801f10 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -13,6 +13,7 @@ import pyrfc3339 from letsencrypt import constants from letsencrypt import crypto_util from letsencrypt import errors +from letsencrypt import error_handler from letsencrypt import le_util ALL_FOUR = ("cert", "privkey", "chain", "fullchain") @@ -129,6 +130,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes self.chain = self.configuration["chain"] self.fullchain = self.configuration["fullchain"] + self._fix_symlinks() + def _consistent(self): """Are the files associated with this lineage self-consistent? @@ -228,7 +231,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes """ lockfiles = self._lockfiles() - if all(os.path.exists(lockfile) for lockfile in lockfiles): + if all(os.path.exists(lockfile[1]) for lockfile in lockfiles): for kind, lockfile in lockfiles: link = getattr(self, kind) if os.path.lexists(link): @@ -414,16 +417,17 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes :param int version: the desired version """ - lockfiles = self._lockfiles() - for kind, lockfile in lockfiles: - with open(lockfile, "w") as f: - f.write("{0}\n".format(self.current_target(kind))) + with error_handler.ErrorHandler(self._fix_symlinks): + lockfiles = self._lockfiles() + for kind, lockfile in lockfiles: + with open(lockfile, "w") as f: + f.write("{0}\n".format(self.current_target(kind))) - for kind in ALL_FOUR: - self._update_link_to(kind, version) + for kind in ALL_FOUR: + self._update_link_to(kind, version) - for _, lockfile in lockfiles: - os.unlink(lockfile) + for _, lockfile in lockfiles: + os.unlink(lockfile) def _notafterbefore(self, method, version): """Internal helper function for finding notbefore/notafter.""" From b90827dc98d5ebe4f7104e1580d295563d33e409 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Oct 2015 20:40:17 -0700 Subject: [PATCH 014/216] Added tests --- letsencrypt/storage.py | 8 ++++---- letsencrypt/tests/renewer_test.py | 34 ++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 7d8801f10..76b54fab3 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -208,8 +208,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # happen as a result of random tampering by a sysadmin, or # filesystem errors, or crashes.) - def _lockfiles(self): - """Returns the kind and path of all used lockfiles. + def _symlink_lockfiles(self): + """Returns the kind and path of all symlink lockfiles. :returns: list of (kind, lockfile) tuples :rtype: list @@ -230,7 +230,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes has no effect. """ - lockfiles = self._lockfiles() + lockfiles = self._symlink_lockfiles() if all(os.path.exists(lockfile[1]) for lockfile in lockfiles): for kind, lockfile in lockfiles: link = getattr(self, kind) @@ -418,7 +418,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes """ with error_handler.ErrorHandler(self._fix_symlinks): - lockfiles = self._lockfiles() + lockfiles = self._symlink_lockfiles() for kind, lockfile in lockfiles: with open(lockfile, "w") as f: f.write("{0}\n".format(self.current_target(kind))) diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index bc8afbcb5..e8ad168e7 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -290,7 +290,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertEqual("cert8.pem", os.path.basename(self.test_rc.version("cert", 8))) - def test_update_all_links_to(self): + def test_update_all_links_to_success(self): for ver in xrange(1, 6): for kind in ALL_FOUR: where = getattr(self.test_rc, kind) @@ -308,6 +308,38 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertEqual(ver, self.test_rc.current_version(kind)) self.assertEqual(self.test_rc.latest_common_version(), 5) + def test_update_all_links_to_partial_failure(self): + def unlink_or_raise(path, real_unlink=os.unlink): + # pylint: disable=missing-docstring + if "fullchain" in path and not path.endswith(".pem"): + raise ValueError + else: + real_unlink(path) + + self._write_out_ex_kinds() + with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink: + mock_unlink.side_effect = unlink_or_raise + self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12) + + for kind in ALL_FOUR: + self.assertEqual(self.test_rc.current_version(kind), 12) + + def test_update_all_links_to_full_failure(self): + def unlink_or_raise(path, real_unlink=os.unlink): + # pylint: disable=missing-docstring + if "fullchain" in path: + raise ValueError + else: + real_unlink(path) + + self._write_out_ex_kinds() + with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink: + mock_unlink.side_effect = unlink_or_raise + self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12) + + for kind in ALL_FOUR: + self.assertEqual(self.test_rc.current_version(kind), 11) + def test_has_pending_deployment(self): for ver in xrange(1, 6): for kind in ALL_FOUR: From acc44f2b659850f6907ea4f7c6ba1a6671eee672 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 12 Oct 2015 21:04:40 -0700 Subject: [PATCH 015/216] Always delete lockfiles in _fix_symlinks --- letsencrypt/storage.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 76b54fab3..207099e74 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -238,10 +238,10 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes os.unlink(link) with open(lockfile) as f: os.symlink(f.readline().rstrip(), link) - else: - for _, lockfile in lockfiles: - if os.path.exists(lockfile): - os.unlink(lockfile) + + for _, lockfile in lockfiles: + if os.path.exists(lockfile): + os.unlink(lockfile) def current_target(self, kind): """Returns full path to which the specified item currently points. From 0e6d652ce37514b721fba5ce929caeb792930b85 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 13 Oct 2015 18:25:31 -0700 Subject: [PATCH 016/216] Move ConfigObj parsing inside of RenewableCert.__init__ --- letsencrypt/cli.py | 17 ++++------- letsencrypt/renewer.py | 30 ++++++------------- letsencrypt/storage.py | 48 ++++++++++++++++--------------- letsencrypt/tests/cli_test.py | 17 ++++++----- letsencrypt/tests/renewer_test.py | 40 +++++++++----------------- 5 files changed, 62 insertions(+), 90 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 07ccd38fd..35113be3a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -12,7 +12,6 @@ import time import traceback import configargparse -import configobj import OpenSSL import zope.component import zope.interface.exceptions @@ -167,25 +166,21 @@ def _init_le_client(args, config, authenticator, installer): return client.Client(config, acc, authenticator, installer, acme=acme) -def _find_duplicative_certs(domains, config, renew_config): +def _find_duplicative_certs(config, domains): """Find existing certs that duplicate the request.""" identical_names_cert, subset_names_cert = None, None - configs_dir = renew_config.renewal_configs_dir + cli_config = configuration.RenewerConfiguration(config) + configs_dir = cli_config.renewal_configs_dir # Verify the directory is there le_util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid()) - cli_config = configuration.RenewerConfiguration(config) for renewal_file in os.listdir(configs_dir): try: full_path = os.path.join(configs_dir, renewal_file) - rc_config = configobj.ConfigObj(renew_config.renewer_config_file) - rc_config.merge(configobj.ConfigObj(full_path)) - rc_config.filename = full_path - candidate_lineage = storage.RenewableCert( - rc_config, config_opts=None, cli_config=cli_config) - except (configobj.ConfigObjError, errors.CertStorageError, IOError): + candidate_lineage = storage.RenewableCert(full_path, cli_config) + except (errors.CertStorageError, IOError): logger.warning("Renewal configuration file %s is broken. " "Skipping.", full_path) continue @@ -217,7 +212,7 @@ def _treat_as_renewal(config, domains): # kind of certificate to be obtained with renewal = False.) if not config.duplicate: ident_names_cert, subset_names_cert = _find_duplicative_certs( - domains, config, configuration.RenewerConfiguration(config)) + config, domains) # I am not sure whether that correctly reads the systemwide # configuration file. question = None diff --git a/letsencrypt/renewer.py b/letsencrypt/renewer.py index 8d8540e6f..f91d60833 100644 --- a/letsencrypt/renewer.py +++ b/letsencrypt/renewer.py @@ -12,7 +12,6 @@ import logging import os import sys -import configobj import OpenSSL import zope.component @@ -141,7 +140,7 @@ def _create_parser(): return _paths_parser(parser) -def main(config=None, cli_args=sys.argv[1:]): +def main(cli_args=sys.argv[1:]): """Main function for autorenewer script.""" # TODO: Distinguish automated invocation from manual invocation, # perhaps by looking at sys.argv[0] and inhibiting automated @@ -149,6 +148,11 @@ def main(config=None, cli_args=sys.argv[1:]): # turned it off. (The boolean parameter should probably be # called renewer_enabled.) + # TODO: When we have a more elaborate renewer command line, we will + # presumably also be able to specify a config file on the + # command line, which, if provided, should take precedence over + # te default config files + zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) args = _create_parser().parse_args(cli_args) @@ -159,28 +163,12 @@ def main(config=None, cli_args=sys.argv[1:]): cli_config = configuration.RenewerConfiguration(args) - config = storage.config_with_defaults(config) - # Now attempt to read the renewer config file and augment or replace - # the renewer defaults with any options contained in that file. If - # renewer_config_file is undefined or if the file is nonexistent or - # empty, this .merge() will have no effect. TODO: when we have a more - # elaborate renewer command line, we will presumably also be able to - # specify a config file on the command line, which, if provided, should - # take precedence over this one. - config.merge(configobj.ConfigObj(cli_config.renewer_config_file)) # Ensure that all of the needed folders have been created before continuing le_util.make_or_verify_dir(cli_config.work_dir, constants.CONFIG_DIRS_MODE, uid) - for i in os.listdir(cli_config.renewal_configs_dir): - print "Processing", i - if not i.endswith(".conf"): - continue - rc_config = configobj.ConfigObj(cli_config.renewer_config_file) - rc_config.merge(configobj.ConfigObj( - os.path.join(cli_config.renewal_configs_dir, i))) - # TODO: this is a dirty hack! - rc_config.filename = os.path.join(cli_config.renewal_configs_dir, i) + for renewal_file in os.listdir(cli_config.renewal_configs_dir): + print "Processing", renewal_file try: # TODO: Before trying to initialize the RenewableCert object, # we could check here whether the combination of the config @@ -190,7 +178,7 @@ def main(config=None, cli_args=sys.argv[1:]): # RenewableCert object for this cert at all, which could # dramatically improve performance for large deployments # where autorenewal is widely turned off. - cert = storage.RenewableCert(rc_config, cli_config=cli_config) + cert = storage.RenewableCert(renewal_file, cli_config) except errors.CertStorageError: # This indicates an invalid renewal configuration file, such # as one missing a required parameter (in the future, perhaps diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 207099e74..9b3290e2c 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -81,49 +81,49 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes renewal configuration file and/or systemwide defaults. """ - def __init__(self, configfile, config_opts=None, cli_config=None): + def __init__(self, config_filename, cli_config): """Instantiate a RenewableCert object from an existing lineage. - :param configobj.ConfigObj configfile: an already-parsed - ConfigObj object made from reading the renewal config file + :param str config_filename: the path to the renewal config file that defines this lineage. - - :param configobj.ConfigObj config_opts: systemwide defaults for - renewal properties not otherwise specified in the individual - renewal config file. - :param .RenewerConfiguration cli_config: + :param .RenewerConfiguration: parsed command line arguments :raises .CertStorageError: if the configuration file's name didn't end in ".conf", or the file is missing or broken. - :raises TypeError: if the provided renewal configuration isn't a - ConfigObj object. """ self.cli_config = cli_config - if isinstance(configfile, configobj.ConfigObj): - if not os.path.basename(configfile.filename).endswith(".conf"): - raise errors.CertStorageError( - "renewal config file name must end in .conf") - self.lineagename = os.path.basename( - configfile.filename)[:-len(".conf")] - else: - raise TypeError("RenewableCert config must be ConfigObj object") + if not config_filename.endswith(".conf"): + raise errors.CertStorageError( + "renewal config file name must end in .conf") + self.lineagename = os.path.basename( + config_filename[:-len(".conf")]) # self.configuration should be used to read parameters that # may have been chosen based on default values from the # systemwide renewal configuration; self.configfile should be # used to make and save changes. - self.configfile = configfile + try: + self.configfile = configobj.ConfigObj(config_filename) + except configobj.ConfigObjError: + raise errors.CertStorageError( + "error parsing {0}".format(config_filename)) + # TODO: Do we actually use anything from defaults and do we want to # read further defaults from the systemwide renewal configuration # file at this stage? - self.configuration = config_with_defaults(config_opts) - self.configuration.merge(self.configfile) + try: + self.configuration = config_with_defaults( + configobj.ConfigObj(cli_config.renewer_config_file)) + self.configuration.merge(self.configfile) + except: + raise errors.CertStorageError( + "error parsing {0}".format(cli_config.renewer_config_file)) if not all(x in self.configuration for x in ALL_FOUR): raise errors.CertStorageError( "renewal config file {0} is missing a required " - "file reference".format(configfile)) + "file reference".format(self.configfile)) self.cert = self.configuration["cert"] self.privkey = self.configuration["privkey"] @@ -622,6 +622,8 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes :param configobj.ConfigObj config: renewal configuration defaults, affecting, for example, the locations of the directories where the associated files will be saved + :param .RenewerConfiguration cli_config: parsed command line + arguments :returns: the newly-created RenewalCert object :rtype: :class:`storage.renewableCert`""" @@ -691,7 +693,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # TODO: add human-readable comments explaining other available # parameters new_config.write() - return cls(new_config, config, cli_config) + return cls(new_config.filename, cli_config) def save_successor(self, prior_version, new_cert, new_privkey, new_chain): """Save new cert and chain as a successor of a prior version. diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index d0fae370d..bc65d3599 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -302,26 +302,25 @@ class DuplicativeCertsTest(renewer_test.BaseRenewableCertTest): f.write(test_cert) # No overlap at all - result = _find_duplicative_certs(['wow.net', 'hooray.org'], - self.config, self.cli_config) + result = _find_duplicative_certs( + self.cli_config, ['wow.net', 'hooray.org']) self.assertEqual(result, (None, None)) # Totally identical - result = _find_duplicative_certs(['example.com', 'www.example.com'], - self.config, self.cli_config) + result = _find_duplicative_certs( + self.cli_config, ['example.com', 'www.example.com']) self.assertTrue(result[0].configfile.filename.endswith('example.org.conf')) self.assertEqual(result[1], None) # Superset - result = _find_duplicative_certs(['example.com', 'www.example.com', - 'something.new'], self.config, - self.cli_config) + result = _find_duplicative_certs( + self.cli_config, ['example.com', 'www.example.com', 'something.new']) self.assertEqual(result[0], None) self.assertTrue(result[1].configfile.filename.endswith('example.org.conf')) # Partial overlap doesn't count - result = _find_duplicative_certs(['example.com', 'something.new'], - self.config, self.cli_config) + result = _find_duplicative_certs( + self.cli_config, ['example.com', 'something.new']) self.assertEqual(result, (None, None)) diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index e8ad168e7..652c54577 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -63,11 +63,11 @@ class BaseRenewableCertTest(unittest.TestCase): kind + ".pem") config.filename = os.path.join(self.tempdir, "renewal", "example.org.conf") + config.write() self.config = config self.defaults = configobj.ConfigObj() - self.test_rc = storage.RenewableCert( - self.config, self.defaults, self.cli_config) + self.test_rc = storage.RenewableCert(config.filename, self.cli_config) def tearDown(self): shutil.rmtree(self.tempdir) @@ -99,34 +99,26 @@ class RenewableCertTests(BaseRenewableCertTest): def test_renewal_bad_config(self): """Test that the RenewableCert constructor will complain if - the renewal configuration file doesn't end in ".conf" or if it - isn't a ConfigObj.""" + the renewal configuration file doesn't end in ".conf" + + """ from letsencrypt import storage - defaults = configobj.ConfigObj() - config = configobj.ConfigObj() - # These files don't exist and aren't created here; the point of the test - # is to confirm that the constructor rejects them outright because of - # the configfile's name. - for kind in ALL_FOUR: - config["cert"] = "nonexistent_" + kind + ".pem" - config.filename = "nonexistent_sillyfile" - self.assertRaises( - errors.CertStorageError, storage.RenewableCert, config, defaults) - self.assertRaises(TypeError, storage.RenewableCert, "fun", defaults) + self.assertRaises(errors.CertStorageError, storage.RenewableCert, + "fun", self.cli_config) def test_renewal_incomplete_config(self): """Test that the RenewableCert constructor will complain if the renewal configuration file is missing a required file element.""" from letsencrypt import storage - defaults = configobj.ConfigObj() config = configobj.ConfigObj() config["cert"] = "imaginary_cert.pem" # Here the required privkey is missing. config["chain"] = "imaginary_chain.pem" config["fullchain"] = "imaginary_fullchain.pem" - config.filename = "imaginary_config.conf" - self.assertRaises( - errors.CertStorageError, storage.RenewableCert, config, defaults) + config.filename = os.path.join(self.tempdir, "imaginary_config.conf") + config.write() + self.assertRaises(errors.CertStorageError, storage.RenewableCert, + config.filename, self.cli_config) def test_consistent(self): # pylint: disable=too-many-statements,protected-access @@ -719,10 +711,6 @@ class RenewableCertTests(BaseRenewableCertTest): mock_rc_instance.should_autorenew.return_value = True mock_rc_instance.latest_common_version.return_value = 10 mock_rc.return_value = mock_rc_instance - with open(os.path.join(self.cli_config.renewal_configs_dir, - "README"), "w") as f: - f.write("This is a README file to make sure that the renewer is") - f.write("able to correctly ignore files that don't end in .conf.") with open(os.path.join(self.cli_config.renewal_configs_dir, "example.org.conf"), "w") as f: # This isn't actually parsed in this test; we have a separate @@ -734,7 +722,7 @@ class RenewableCertTests(BaseRenewableCertTest): "example.com.conf"), "w") as f: f.write("cert = cert.pem\nprivkey = privkey.pem\n") f.write("chain = chain.pem\nfullchain = fullchain.pem\n") - renewer.main(self.defaults, cli_args=self._common_cli_args()) + renewer.main(cli_args=self._common_cli_args()) self.assertEqual(mock_rc.call_count, 2) self.assertEqual(mock_rc_instance.update_all_links_to.call_count, 2) self.assertEqual(mock_notify.notify.call_count, 4) @@ -747,7 +735,7 @@ class RenewableCertTests(BaseRenewableCertTest): mock_happy_instance.should_autorenew.return_value = False mock_happy_instance.latest_common_version.return_value = 10 mock_rc.return_value = mock_happy_instance - renewer.main(self.defaults, cli_args=self._common_cli_args()) + renewer.main(cli_args=self._common_cli_args()) self.assertEqual(mock_rc.call_count, 4) self.assertEqual(mock_happy_instance.update_all_links_to.call_count, 0) self.assertEqual(mock_notify.notify.call_count, 4) @@ -758,7 +746,7 @@ class RenewableCertTests(BaseRenewableCertTest): with open(os.path.join(self.cli_config.renewal_configs_dir, "bad.conf"), "w") as f: f.write("incomplete = configfile\n") - renewer.main(self.defaults, cli_args=self._common_cli_args()) + renewer.main(cli_args=self._common_cli_args()) # The errors.CertStorageError is caught inside and nothing happens. From 2239ae9a68d8b8e0b6e4400fe7bb80d3d33e6c54 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 13 Oct 2015 18:39:23 -0700 Subject: [PATCH 017/216] Don't read global renewer config file --- letsencrypt/storage.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 9b3290e2c..530cf9d0f 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -108,17 +108,10 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes except configobj.ConfigObjError: raise errors.CertStorageError( "error parsing {0}".format(config_filename)) - # TODO: Do we actually use anything from defaults and do we want to # read further defaults from the systemwide renewal configuration # file at this stage? - try: - self.configuration = config_with_defaults( - configobj.ConfigObj(cli_config.renewer_config_file)) - self.configuration.merge(self.configfile) - except: - raise errors.CertStorageError( - "error parsing {0}".format(cli_config.renewer_config_file)) + self.configuration = config_with_defaults(self.configfile) if not all(x in self.configuration for x in ALL_FOUR): raise errors.CertStorageError( From 00e5515fe63bc98f6cc7721863a035eae4b2c642 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 13 Oct 2015 19:04:25 -0700 Subject: [PATCH 018/216] Added broken config test --- letsencrypt/tests/renewer_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index 652c54577..65ca68d36 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -103,6 +103,12 @@ class RenewableCertTests(BaseRenewableCertTest): """ from letsencrypt import storage + broken = os.path.join(self.tempdir, "broken.conf") + with open(broken, "w") as f: + f.write("[No closing bracket for you!") + self.assertRaises(errors.CertStorageError, storage.RenewableCert, + broken, self.cli_config) + os.unlink(broken) self.assertRaises(errors.CertStorageError, storage.RenewableCert, "fun", self.cli_config) From 29ee633dede35807bb0f972239fb3f9145c6a0d9 Mon Sep 17 00:00:00 2001 From: Luke Rogers Date: Thu, 15 Oct 2015 14:12:38 +1300 Subject: [PATCH 019/216] Tidy up a list in the readme. Just capitalising a few things. --- README.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 43ecd413c..4040ca284 100644 --- a/README.rst +++ b/README.rst @@ -76,23 +76,23 @@ server automatically!:: Current Features ---------------- -* web servers supported: +* Supports multiple web servers: - apache/2.x (tested and working on Ubuntu Linux) - nginx/0.8.48+ (under development) - - standalone (runs its own webserver to prove you control the domain) + - standalone (runs it's own simple webserver to prove you control a domain) -* the private key is generated locally on your system -* can talk to the Let's Encrypt (demo) CA or optionally to other ACME - compliant services -* can get domain-validated (DV) certificates -* can revoke certificates -* adjustable RSA key bitlength (2048 (default), 4096, ...) -* optionally can install a http->https redirect, so your site effectively +* The private key is generated locally on your system. +* Can talk to the Let's Encrypt (demo) CA or optionally to other ACME + compliant services. +* Can get domain-validated (DV) certificates. +* Can revoke certificates. +* Adjustable RSA key bit-length (2048 (default), 4096, ...). +* Can optionally install a http -> https redirect, so your site effectively runs https only (Apache only) -* fully automated -* configuration changes are logged and can be reverted using the CLI -* text and ncurses UI +* Fully automated. +* Configuration changes are logged and can be reverted. +* Text and ncurses UI. * Free and Open Source Software, made with Python. From 83a7d4d7922efb9ad8211a98985b16fcaaa2561f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Sat, 17 Oct 2015 17:26:52 -0700 Subject: [PATCH 020/216] changes += schoen's feedback --- letsencrypt/storage.py | 48 +++++++++++++++---------------- letsencrypt/tests/renewer_test.py | 2 +- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 530cf9d0f..110b20dd2 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -201,20 +201,20 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # happen as a result of random tampering by a sysadmin, or # filesystem errors, or crashes.) - def _symlink_lockfiles(self): - """Returns the kind and path of all symlink lockfiles. + def _previous_symlinks(self): + """Returns the kind and path of all symlinks used in recovery. - :returns: list of (kind, lockfile) tuples + :returns: list of (kind, symlink) tuples :rtype: list """ - lockfiles = [] + previous_symlinks = [] for kind in ALL_FOUR: - lock_dir = os.path.dirname(getattr(self, kind)) - lock_base = "{0}.lock".format(kind) - lockfiles.append((kind, os.path.join(lock_dir, lock_base))) + link_dir = os.path.dirname(getattr(self, kind)) + link_base = "previous_{0}.pem".format(kind) + previous_symlinks.append((kind, os.path.join(link_dir, link_base))) - return lockfiles + return previous_symlinks def _fix_symlinks(self): """Fixes symlinks in the event of an incomplete version update. @@ -223,18 +223,17 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes has no effect. """ - lockfiles = self._symlink_lockfiles() - if all(os.path.exists(lockfile[1]) for lockfile in lockfiles): - for kind, lockfile in lockfiles: - link = getattr(self, kind) - if os.path.lexists(link): - os.unlink(link) - with open(lockfile) as f: - os.symlink(f.readline().rstrip(), link) + previous_symlinks = self._previous_symlinks() + if all(os.path.exists(link[1]) for link in previous_symlinks): + for kind, previous_link in previous_symlinks: + current_link = getattr(self, kind) + if os.path.lexists(current_link): + os.unlink(current_link) + os.symlink(os.readlink(previous_link), current_link) - for _, lockfile in lockfiles: - if os.path.exists(lockfile): - os.unlink(lockfile) + for _, link in previous_symlinks: + if os.path.exists(link): + os.unlink(link) def current_target(self, kind): """Returns full path to which the specified item currently points. @@ -411,16 +410,15 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes """ with error_handler.ErrorHandler(self._fix_symlinks): - lockfiles = self._symlink_lockfiles() - for kind, lockfile in lockfiles: - with open(lockfile, "w") as f: - f.write("{0}\n".format(self.current_target(kind))) + previous_links = self._previous_symlinks() + for kind, link in previous_links: + os.symlink(self.current_target(kind), link) for kind in ALL_FOUR: self._update_link_to(kind, version) - for _, lockfile in lockfiles: - os.unlink(lockfile) + for _, link in previous_links: + os.unlink(link) def _notafterbefore(self, method, version): """Internal helper function for finding notbefore/notafter.""" diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index 65ca68d36..91eb36ac8 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -309,7 +309,7 @@ class RenewableCertTests(BaseRenewableCertTest): def test_update_all_links_to_partial_failure(self): def unlink_or_raise(path, real_unlink=os.unlink): # pylint: disable=missing-docstring - if "fullchain" in path and not path.endswith(".pem"): + if "fullchain" in path and "prev" in path: raise ValueError else: real_unlink(path) From 594613e2ce92757a8f590a4dc2139a529259d878 Mon Sep 17 00:00:00 2001 From: Whyfoo Date: Wed, 21 Oct 2015 20:58:33 +0200 Subject: [PATCH 021/216] Name fix --- letsencrypt-nginx/letsencrypt_nginx/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/letsencrypt-nginx/letsencrypt_nginx/parser.py index fc8ed95f1..19483821a 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/parser.py +++ b/letsencrypt-nginx/letsencrypt_nginx/parser.py @@ -491,5 +491,5 @@ def _add_directives(block, directives, replace=False): changed = True if not changed: raise errors.MisconfigurationError( - 'LetsEncrypt expected directive for %s in the Nginx ' + 'Let\'s Encrypt expected directive for %s in the Nginx ' 'config but did not find it.' % directive[0]) From 059ca6dbce361403e52dade0eae2e6a55dc1d6c8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 12:23:00 -0700 Subject: [PATCH 022/216] Make debian bootstrapper more agnostic about venv packaging Closes: #1061 --- bootstrap/_deb_common.sh | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 687f30d09..f6467cf99 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -12,46 +12,33 @@ # - Raspbian: # - 7.8 (armhf) +apt-get update # virtualenv binary can be found in different packages depending on # distro version (#346) -newer () { - apt-get install -y lsb-release --no-install-recommends - distro=$(lsb_release -si) - # 6.0.10 => 60, 14.04 => 1404 - # TODO: in sid version==unstable - version=$(lsb_release -sr | awk -F '.' '{print $1 $2}') - if [ "$distro" = "Ubuntu" -a "$version" -ge 1410 ] - then - return 0; - elif [ "$distro" = "Debian" -a "$version" -ge 80 ] - then - return 0; - else - return 1; - fi -} -apt-get update - -# you can force newer if lsb_release is not available (e.g. Docker -# debian:jessie base image) -if [ "$1" = "newer" ] || newer -then +virtualenv= +if apt-cache show virtualenv > /dev/null ; then virtualenv="virtualenv" -else - virtualenv="python-virtualenv" fi +if apt-cache show python-virtualenv > /dev/null ; then + virtualenv="$virualenv python-virtualenv" +fi apt-get install -y --no-install-recommends \ git-core \ python \ python-dev \ - "$virtualenv" \ + $virtualenv \ gcc \ dialog \ libaugeas0 \ libssl-dev \ libffi-dev \ ca-certificates \ + +if ! which virtualenv > /dev/null ; then + echo Failed to install a working \"virtualenv\" command, exiting + exit 1 +fi From 41a59c0e15a05b14c3b6df6db966b148c13a2a79 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 12:27:22 -0700 Subject: [PATCH 023/216] typo --- bootstrap/_deb_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index f6467cf99..ae34dea37 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -23,7 +23,7 @@ if apt-cache show virtualenv > /dev/null ; then fi if apt-cache show python-virtualenv > /dev/null ; then - virtualenv="$virualenv python-virtualenv" + virtualenv="$virtualenv python-virtualenv" fi apt-get install -y --no-install-recommends \ From 7ce1da56d53c302c9021380385151269f0d80105 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 15:59:20 -0700 Subject: [PATCH 024/216] Point the user to fullchain.pem, not cert.pem Closes: #1024 --- letsencrypt/cli.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 1b396b0b8..31891dd5d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -267,16 +267,22 @@ def _treat_as_renewal(config, domains): return None -def _report_new_cert(cert_path): - """Reports the creation of a new certificate to the user.""" - expiry = crypto_util.notAfter(cert_path).date() +def _report_new_cert(lineage): + """ + Reports the creation of a new certificate to the user. + :param RenewableCert lineage: the lineage of the new cert + """ + expiry = crypto_util.notAfter(lineage.cert).date() reporter_util = zope.component.getUtility(interfaces.IReporter) - reporter_util.add_message("Congratulations! Your certificate has been " - "saved at {0} and will expire on {1}. To obtain " - "a new version of the certificate in the " - "future, simply run Let's Encrypt again.".format( - cert_path, expiry), - reporter_util.MEDIUM_PRIORITY) + # Tell the user about fullchain.pem because that's what modern webservers + # (Nginx and Apache2.4) will want. + # XXX Perhaps one day we could detect the presence of known old webservers + # and say something more informative here. + msg = ("Congratulations! Your certificate and chain have been saved at {0}." + " Your cert will expire on {1}. To obtain a new version of the " + "certificate in the future, simply run Let's Encrypt again." + .format(lineage.fullchain, expiry)) + reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) def _auth_from_domains(le_client, config, domains, plugins): @@ -304,7 +310,7 @@ def _auth_from_domains(le_client, config, domains, plugins): if not lineage: raise Error("Certificate could not be obtained") - _report_new_cert(lineage.cert) + _report_new_cert(lineage) return lineage From 6382b184f073337f16d4e68480d5a800beea9179 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 15:57:37 -0700 Subject: [PATCH 025/216] Update documentation of tested platforms --- bootstrap/_deb_common.sh | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index ae34dea37..2f44c4e91 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -1,16 +1,22 @@ #!/bin/sh -# Tested with: -# - Ubuntu: -# - 12.04 (x64, Travis) -# - 14.04 (x64, Vagrant) -# - 14.10 (x64) -# - Debian: -# - 6.0.10 "squeeze" (x64) -# - 7.8 "wheezy" (x64) -# - 8.0 "jessie" (x64) -# - Raspbian: -# - 7.8 (armhf) +# Current version tested with: +# +# - Ubuntu +# - 14.04 (x64) +# - 15.04 (x64) +# - Debian +# - 7.9 "wheezy" (x64) +# - sid (2015-10-21) (x64) + +# Past versions tested with: +# +# - Debian 8.0 "jessie" (x64) +# - Raspbian 7.8 (armhf) + +# Believed not to work: +# +# - Debian 6.0.10 "squeeze" (x64) apt-get update From 934301abc6f3925294ffc03e3be3c2b5dcb61a67 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 13:50:08 +0000 Subject: [PATCH 026/216] Fix ACME module description --- acme/README.rst | 2 +- acme/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/acme/README.rst b/acme/README.rst index e3ca8b738..4b6ea818c 100644 --- a/acme/README.rst +++ b/acme/README.rst @@ -1 +1 @@ -ACME protocol implementation for Python +ACME protocol implementation in Python diff --git a/acme/setup.py b/acme/setup.py index 6448b7fe9..6e27890b1 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -41,7 +41,7 @@ testing_extras = [ setup( name='acme', version=version, - description='ACME protocol implementation', + description='ACME protocol implementation in Python', url='https://github.com/letsencrypt/letsencrypt', author="Let's Encrypt Project", author_email='client-dev@letsencrypt.org', From b9868d3c9748e28207f7ee3e66f6622333f4f8d7 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 13:42:17 +0000 Subject: [PATCH 027/216] sphinx-quickstart for subpkgs ``` sphinx-quickstart --dot _ --project acme-python --author Let's Encrypt Project -v 0 --release 0 --language en --suffix .rst --master index --ext-autodoc --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode --makefile --batchfile acme/docs sphinx-quickstart --dot _ --project letsencrypt-apache --author Let's Encrypt Project -v 0 --release 0 --language en --suffix .rst --master index --ext-autodoc --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode --makefile --batchfile letsencrypt-apache/docs sphinx-quickstart --dot _ --project letsencrypt-nginx --author Let's Encrypt Project -v 0 --release 0 --language en --suffix .rst --master index --ext-autodoc --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode --makefile --batchfile letsencrypt-nginx/docs sphinx-quickstart --dot _ --project letshelp-letsencrypt --author Let's Encrypt Project -v 0 --release 0 --language en --suffix .rst --master index --ext-autodoc --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode --makefile --batchfile letshelp-letsencrypt/docs sphinx-quickstart --dot _ --project letsencrypt-compatibility-test --author Let's Encrypt Project -v 0 --release 0 --language en --suffix .rst --master index --ext-autodoc --ext-intersphinx --ext-todo --ext-coverage --ext-viewcode --makefile --batchfile letsencrypt-compatibility-test/docs ``` --- acme/docs/Makefile | 192 ++++++++++++ acme/docs/conf.py | 294 ++++++++++++++++++ acme/docs/index.rst | 22 ++ acme/docs/make.bat | 263 ++++++++++++++++ letsencrypt-apache/docs/Makefile | 192 ++++++++++++ letsencrypt-apache/docs/conf.py | 294 ++++++++++++++++++ letsencrypt-apache/docs/index.rst | 22 ++ letsencrypt-apache/docs/make.bat | 263 ++++++++++++++++ letsencrypt-compatibility-test/docs/Makefile | 192 ++++++++++++ letsencrypt-compatibility-test/docs/conf.py | 294 ++++++++++++++++++ letsencrypt-compatibility-test/docs/index.rst | 22 ++ letsencrypt-compatibility-test/docs/make.bat | 263 ++++++++++++++++ letsencrypt-nginx/docs/Makefile | 192 ++++++++++++ letsencrypt-nginx/docs/conf.py | 294 ++++++++++++++++++ letsencrypt-nginx/docs/index.rst | 22 ++ letsencrypt-nginx/docs/make.bat | 263 ++++++++++++++++ letshelp-letsencrypt/docs/Makefile | 192 ++++++++++++ letshelp-letsencrypt/docs/conf.py | 294 ++++++++++++++++++ letshelp-letsencrypt/docs/index.rst | 22 ++ letshelp-letsencrypt/docs/make.bat | 263 ++++++++++++++++ 20 files changed, 3855 insertions(+) create mode 100644 acme/docs/Makefile create mode 100644 acme/docs/conf.py create mode 100644 acme/docs/index.rst create mode 100644 acme/docs/make.bat create mode 100644 letsencrypt-apache/docs/Makefile create mode 100644 letsencrypt-apache/docs/conf.py create mode 100644 letsencrypt-apache/docs/index.rst create mode 100644 letsencrypt-apache/docs/make.bat create mode 100644 letsencrypt-compatibility-test/docs/Makefile create mode 100644 letsencrypt-compatibility-test/docs/conf.py create mode 100644 letsencrypt-compatibility-test/docs/index.rst create mode 100644 letsencrypt-compatibility-test/docs/make.bat create mode 100644 letsencrypt-nginx/docs/Makefile create mode 100644 letsencrypt-nginx/docs/conf.py create mode 100644 letsencrypt-nginx/docs/index.rst create mode 100644 letsencrypt-nginx/docs/make.bat create mode 100644 letshelp-letsencrypt/docs/Makefile create mode 100644 letshelp-letsencrypt/docs/conf.py create mode 100644 letshelp-letsencrypt/docs/index.rst create mode 100644 letshelp-letsencrypt/docs/make.bat diff --git a/acme/docs/Makefile b/acme/docs/Makefile new file mode 100644 index 000000000..79de9c0a3 --- /dev/null +++ b/acme/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/acme-python.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/acme-python.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/acme-python" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/acme-python" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/acme/docs/conf.py b/acme/docs/conf.py new file mode 100644 index 000000000..0b7dee0e1 --- /dev/null +++ b/acme/docs/conf.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +# +# acme-python documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 18 13:38:06 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +# -- 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', +] + +# 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 encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'acme-python' +copyright = u'2015, Let\'s Encrypt Project' +author = u'Let\'s Encrypt Project' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0' +# The full version, including alpha/beta/rc tags. +release = '0' + +# 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 = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# 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 themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'acme-pythondoc' + +# -- 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, 'acme-python.tex', u'acme-python Documentation', + u'Let\'s Encrypt Project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- 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, 'acme-python', u'acme-python Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- 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, 'acme-python', u'acme-python Documentation', + author, 'acme-python', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/acme/docs/index.rst b/acme/docs/index.rst new file mode 100644 index 000000000..357d73fd0 --- /dev/null +++ b/acme/docs/index.rst @@ -0,0 +1,22 @@ +.. acme-python documentation master file, created by + sphinx-quickstart on Sun Oct 18 13:38:06 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to acme-python's documentation! +======================================= + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/acme/docs/make.bat b/acme/docs/make.bat new file mode 100644 index 000000000..781185977 --- /dev/null +++ b/acme/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 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 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\acme-python.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\acme-python.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/letsencrypt-apache/docs/Makefile b/letsencrypt-apache/docs/Makefile new file mode 100644 index 000000000..9bf5154fe --- /dev/null +++ b/letsencrypt-apache/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letsencrypt-apache.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-apache.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/letsencrypt-apache" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letsencrypt-apache" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py new file mode 100644 index 000000000..f572d2a7f --- /dev/null +++ b/letsencrypt-apache/docs/conf.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +# +# letsencrypt-apache documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 18 13:39:26 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +# -- 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', +] + +# 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 encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'letsencrypt-apache' +copyright = u'2015, Let\'s Encrypt Project' +author = u'Let\'s Encrypt Project' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0' +# The full version, including alpha/beta/rc tags. +release = '0' + +# 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 = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# 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 themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'letsencrypt-apachedoc' + +# -- 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, 'letsencrypt-apache.tex', u'letsencrypt-apache Documentation', + u'Let\'s Encrypt Project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- 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, 'letsencrypt-apache', u'letsencrypt-apache Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- 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, 'letsencrypt-apache', u'letsencrypt-apache Documentation', + author, 'letsencrypt-apache', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/letsencrypt-apache/docs/index.rst b/letsencrypt-apache/docs/index.rst new file mode 100644 index 000000000..64f0e3e14 --- /dev/null +++ b/letsencrypt-apache/docs/index.rst @@ -0,0 +1,22 @@ +.. letsencrypt-apache documentation master file, created by + sphinx-quickstart on Sun Oct 18 13:39:26 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to letsencrypt-apache's documentation! +============================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/letsencrypt-apache/docs/make.bat b/letsencrypt-apache/docs/make.bat new file mode 100644 index 000000000..62a54fd2c --- /dev/null +++ b/letsencrypt-apache/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 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 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letsencrypt-apache.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-apache.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/letsencrypt-compatibility-test/docs/Makefile b/letsencrypt-compatibility-test/docs/Makefile new file mode 100644 index 000000000..90582a59b --- /dev/null +++ b/letsencrypt-compatibility-test/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letsencrypt-compatibility-test.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-compatibility-test.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/letsencrypt-compatibility-test" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letsencrypt-compatibility-test" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py new file mode 100644 index 000000000..2c468a763 --- /dev/null +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +# +# letsencrypt-compatibility-test documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 18 13:40:53 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +# -- 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', +] + +# 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 encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'letsencrypt-compatibility-test' +copyright = u'2015, Let\'s Encrypt Project' +author = u'Let\'s Encrypt Project' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0' +# The full version, including alpha/beta/rc tags. +release = '0' + +# 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 = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# 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 themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'letsencrypt-compatibility-testdoc' + +# -- 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, 'letsencrypt-compatibility-test.tex', u'letsencrypt-compatibility-test Documentation', + u'Let\'s Encrypt Project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- 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, 'letsencrypt-compatibility-test', u'letsencrypt-compatibility-test Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- 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, 'letsencrypt-compatibility-test', u'letsencrypt-compatibility-test Documentation', + author, 'letsencrypt-compatibility-test', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/letsencrypt-compatibility-test/docs/index.rst b/letsencrypt-compatibility-test/docs/index.rst new file mode 100644 index 000000000..3df442c65 --- /dev/null +++ b/letsencrypt-compatibility-test/docs/index.rst @@ -0,0 +1,22 @@ +.. letsencrypt-compatibility-test documentation master file, created by + sphinx-quickstart on Sun Oct 18 13:40:53 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to letsencrypt-compatibility-test's documentation! +========================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/letsencrypt-compatibility-test/docs/make.bat b/letsencrypt-compatibility-test/docs/make.bat new file mode 100644 index 000000000..c75269bdc --- /dev/null +++ b/letsencrypt-compatibility-test/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 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 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letsencrypt-compatibility-test.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-compatibility-test.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/letsencrypt-nginx/docs/Makefile b/letsencrypt-nginx/docs/Makefile new file mode 100644 index 000000000..3a3828235 --- /dev/null +++ b/letsencrypt-nginx/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letsencrypt-nginx.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-nginx.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/letsencrypt-nginx" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letsencrypt-nginx" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py new file mode 100644 index 000000000..4cde40314 --- /dev/null +++ b/letsencrypt-nginx/docs/conf.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +# +# letsencrypt-nginx documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 18 13:39:39 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +# -- 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', +] + +# 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 encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'letsencrypt-nginx' +copyright = u'2015, Let\'s Encrypt Project' +author = u'Let\'s Encrypt Project' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0' +# The full version, including alpha/beta/rc tags. +release = '0' + +# 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 = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# 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 themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'letsencrypt-nginxdoc' + +# -- 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, 'letsencrypt-nginx.tex', u'letsencrypt-nginx Documentation', + u'Let\'s Encrypt Project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- 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, 'letsencrypt-nginx', u'letsencrypt-nginx Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- 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, 'letsencrypt-nginx', u'letsencrypt-nginx Documentation', + author, 'letsencrypt-nginx', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/letsencrypt-nginx/docs/index.rst b/letsencrypt-nginx/docs/index.rst new file mode 100644 index 000000000..c4822af52 --- /dev/null +++ b/letsencrypt-nginx/docs/index.rst @@ -0,0 +1,22 @@ +.. letsencrypt-nginx documentation master file, created by + sphinx-quickstart on Sun Oct 18 13:39:39 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to letsencrypt-nginx's documentation! +============================================= + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/letsencrypt-nginx/docs/make.bat b/letsencrypt-nginx/docs/make.bat new file mode 100644 index 000000000..eb19a3fb5 --- /dev/null +++ b/letsencrypt-nginx/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 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 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letsencrypt-nginx.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-nginx.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/letshelp-letsencrypt/docs/Makefile b/letshelp-letsencrypt/docs/Makefile new file mode 100644 index 000000000..8e742d837 --- /dev/null +++ b/letshelp-letsencrypt/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/letshelp-letsencrypt.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letshelp-letsencrypt.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/letshelp-letsencrypt" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letshelp-letsencrypt" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py new file mode 100644 index 000000000..cd1ae4531 --- /dev/null +++ b/letshelp-letsencrypt/docs/conf.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +# +# letshelp-letsencrypt documentation build configuration file, created by +# sphinx-quickstart on Sun Oct 18 13:40:19 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# 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. +#sys.path.insert(0, os.path.abspath('.')) + +# -- 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', +] + +# 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 encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'letshelp-letsencrypt' +copyright = u'2015, Let\'s Encrypt Project' +author = u'Let\'s Encrypt Project' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0' +# The full version, including alpha/beta/rc tags. +release = '0' + +# 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 = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'alabaster' + +# 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 themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'letshelp-letsencryptdoc' + +# -- 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, 'letshelp-letsencrypt.tex', u'letshelp-letsencrypt Documentation', + u'Let\'s Encrypt Project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- 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, 'letshelp-letsencrypt', u'letshelp-letsencrypt Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- 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, 'letshelp-letsencrypt', u'letshelp-letsencrypt Documentation', + author, 'letshelp-letsencrypt', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# Example configuration for intersphinx: refer to the Python standard library. +intersphinx_mapping = {'https://docs.python.org/': None} diff --git a/letshelp-letsencrypt/docs/index.rst b/letshelp-letsencrypt/docs/index.rst new file mode 100644 index 000000000..db806d874 --- /dev/null +++ b/letshelp-letsencrypt/docs/index.rst @@ -0,0 +1,22 @@ +.. letshelp-letsencrypt documentation master file, created by + sphinx-quickstart on Sun Oct 18 13:40:19 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to letshelp-letsencrypt's documentation! +================================================ + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/letshelp-letsencrypt/docs/make.bat b/letshelp-letsencrypt/docs/make.bat new file mode 100644 index 000000000..006f7825d --- /dev/null +++ b/letshelp-letsencrypt/docs/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 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 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\letshelp-letsencrypt.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letshelp-letsencrypt.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end From 635008f5e69dadde3240972661bbd437f0b2f4a5 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:12:39 +0000 Subject: [PATCH 028/216] Configure intersphinx --- acme/docs/conf.py | 7 ++++--- docs/conf.py | 8 +++++--- letsencrypt-apache/docs/conf.py | 9 ++++++--- letsencrypt-compatibility-test/docs/conf.py | 11 ++++++++--- letsencrypt-nginx/docs/conf.py | 9 ++++++--- letshelp-letsencrypt/docs/conf.py | 9 ++++++--- 6 files changed, 35 insertions(+), 18 deletions(-) diff --git a/acme/docs/conf.py b/acme/docs/conf.py index 0b7dee0e1..21a412ec0 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -24,7 +24,7 @@ import shlex # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +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 @@ -290,5 +290,6 @@ texinfo_documents = [ #texinfo_no_detailmenu = False -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/', None), +} diff --git a/docs/conf.py b/docs/conf.py index e2b360a6e..9a649e2de 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,7 +42,7 @@ for pkg in 'acme', 'letsencrypt-apache', 'letsencrypt-nginx': # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +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 @@ -320,7 +320,9 @@ texinfo_documents = [ #texinfo_no_detailmenu = False -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'http://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/': None), + 'acme': ('https://acme-python.readthedocs.org', None), +} todo_include_todos = True diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index f572d2a7f..4a0bf16c8 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -24,7 +24,7 @@ import shlex # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +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 @@ -290,5 +290,8 @@ texinfo_documents = [ #texinfo_no_detailmenu = False -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/', None), + 'acme': ('https://acme-python.readthedocs.org', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), +} diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 2c468a763..3cd77fe0e 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -24,7 +24,7 @@ import shlex # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +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 @@ -290,5 +290,10 @@ texinfo_documents = [ #texinfo_no_detailmenu = False -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/', None), + 'acme': ('https://acme-python.readthedocs.org', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), + 'letsencrypt-apache': ('https://letsencrypt-apache.readthedocs.org', None), + 'letsencrypt-nginx': ('https://letsencrypt-nginx.readthedocs.org', None), +} diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index 4cde40314..f9924894b 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -24,7 +24,7 @@ import shlex # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +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 @@ -290,5 +290,8 @@ texinfo_documents = [ #texinfo_no_detailmenu = False -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/', None), + 'acme': ('https://acme-python.readthedocs.org', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), +} diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index cd1ae4531..96de8890e 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -24,7 +24,7 @@ import shlex # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +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 @@ -290,5 +290,8 @@ texinfo_documents = [ #texinfo_no_detailmenu = False -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = { + 'python': ('https://docs.python.org/', None), + 'acme': ('https://acme-python.readthedocs.org', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), +} From 511eef130bf7d41c6b44b65d389f67506ce1413e Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:15:32 +0000 Subject: [PATCH 029/216] Unify todo_include_todos --- docs/conf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 9a649e2de..f96c48352 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -126,6 +126,9 @@ pygments_style = 'sphinx' # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + # -- Options for HTML output ---------------------------------------------- @@ -324,5 +327,3 @@ intersphinx_mapping = { 'python': ('https://docs.python.org/': None), 'acme': ('https://acme-python.readthedocs.org', None), } - -todo_include_todos = True From 607ea59fd35c8c039c44b4a7022bf0347985507f Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:04:46 +0000 Subject: [PATCH 030/216] Add extensions for subpkgs docs --- acme/docs/conf.py | 1 + letsencrypt-compatibility-test/docs/conf.py | 1 + 2 files changed, 2 insertions(+) diff --git a/acme/docs/conf.py b/acme/docs/conf.py index 21a412ec0..7b243771f 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -35,6 +35,7 @@ extensions = [ 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', + 'sphinxcontrib.programoutput', ] # Add any paths that contain templates here, relative to this directory. diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 3cd77fe0e..a118bedf1 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -35,6 +35,7 @@ extensions = [ 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', + 'repoze.sphinx.autointerface', ] # Add any paths that contain templates here, relative to this directory. From 1a6fc9ce76c1659b745eae5dbc802f2421ee89e7 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:21:04 +0000 Subject: [PATCH 031/216] RTD theme everywhere! --- acme/docs/conf.py | 10 +++++++++- letsencrypt-apache/docs/conf.py | 10 +++++++++- letsencrypt-compatibility-test/docs/conf.py | 10 +++++++++- letsencrypt-nginx/docs/conf.py | 10 +++++++++- letshelp-letsencrypt/docs/conf.py | 10 +++++++++- 5 files changed, 45 insertions(+), 5 deletions(-) diff --git a/acme/docs/conf.py b/acme/docs/conf.py index 7b243771f..c4ee0c740 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -115,7 +115,15 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' + +# 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 diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index 4a0bf16c8..8dbffdcd8 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -114,7 +114,15 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' + +# 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 diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index a118bedf1..554e71315 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -115,7 +115,15 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' + +# 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 diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index f9924894b..30093408e 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -114,7 +114,15 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' + +# 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 diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index 96de8890e..8e7890709 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -114,7 +114,15 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' + +# 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 From 1f0cbda9fc3fb55a1427e976d89f4c52c4be08be Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:26:16 +0000 Subject: [PATCH 032/216] Unify autodoc options --- acme/docs/conf.py | 3 +++ letsencrypt-apache/docs/conf.py | 3 +++ letsencrypt-compatibility-test/docs/conf.py | 3 +++ letsencrypt-nginx/docs/conf.py | 3 +++ letshelp-letsencrypt/docs/conf.py | 3 +++ 5 files changed, 15 insertions(+) diff --git a/acme/docs/conf.py b/acme/docs/conf.py index c4ee0c740..b77b87c45 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -38,6 +38,9 @@ extensions = [ 'sphinxcontrib.programoutput', ] +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'] diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index 8dbffdcd8..1f5838fcc 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -37,6 +37,9 @@ extensions = [ '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'] diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 554e71315..066596de7 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -38,6 +38,9 @@ extensions = [ 'repoze.sphinx.autointerface', ] +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'] diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index 30093408e..9716ae6d3 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -37,6 +37,9 @@ extensions = [ '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'] diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index 8e7890709..e9d50855b 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -37,6 +37,9 @@ extensions = [ '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'] From dfdb64c505ed0f16c7d32afeef4a457593fb6edb Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:31:06 +0000 Subject: [PATCH 033/216] sphinx: default_role py:obj --- acme/docs/conf.py | 2 +- letsencrypt-apache/docs/conf.py | 2 +- letsencrypt-compatibility-test/docs/conf.py | 2 +- letsencrypt-nginx/docs/conf.py | 2 +- letshelp-letsencrypt/docs/conf.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/acme/docs/conf.py b/acme/docs/conf.py index b77b87c45..47f9321f9 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -88,7 +88,7 @@ exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index 1f5838fcc..8d57abb0e 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -87,7 +87,7 @@ exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 066596de7..5236996d2 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -88,7 +88,7 @@ exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index 9716ae6d3..d19a73209 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -87,7 +87,7 @@ exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index e9d50855b..d7fc03ddc 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -87,7 +87,7 @@ exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True From 86f01d1fa9a62b95b2db407d4859ae501744799c Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:33:11 +0000 Subject: [PATCH 034/216] sphinx: copyright 2014-2015 --- acme/docs/conf.py | 2 +- docs/conf.py | 2 +- letsencrypt-apache/docs/conf.py | 2 +- letsencrypt-compatibility-test/docs/conf.py | 2 +- letsencrypt-nginx/docs/conf.py | 2 +- letshelp-letsencrypt/docs/conf.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/acme/docs/conf.py b/acme/docs/conf.py index 47f9321f9..dc5dec0ff 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -57,7 +57,7 @@ master_doc = 'index' # General information about the project. project = u'acme-python' -copyright = u'2015, Let\'s Encrypt Project' +copyright = u'2015-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for diff --git a/docs/conf.py b/docs/conf.py index f96c48352..2de0dd877 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -74,7 +74,7 @@ master_doc = 'index' # General information about the project. project = u'Let\'s Encrypt' -copyright = u'2014, Let\'s Encrypt Project' +copyright = u'2014-2015, Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index 8d57abb0e..f578531db 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -56,7 +56,7 @@ master_doc = 'index' # General information about the project. project = u'letsencrypt-apache' -copyright = u'2015, Let\'s Encrypt Project' +copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 5236996d2..4ec0a1d9a 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -57,7 +57,7 @@ master_doc = 'index' # General information about the project. project = u'letsencrypt-compatibility-test' -copyright = u'2015, Let\'s Encrypt Project' +copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index d19a73209..e8e320542 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -56,7 +56,7 @@ master_doc = 'index' # General information about the project. project = u'letsencrypt-nginx' -copyright = u'2015, Let\'s Encrypt Project' +copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index d7fc03ddc..3561a12d1 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -56,7 +56,7 @@ master_doc = 'index' # General information about the project. project = u'letshelp-letsencrypt' -copyright = u'2015, Let\'s Encrypt Project' +copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' # The version info for the project you're documenting, acts as replacement for From baa6c4aeec6401de31a054c5c95940eff250d073 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 14:50:46 +0000 Subject: [PATCH 035/216] gitignores for various doc files --- acme/docs/.gitignore | 1 + acme/docs/_static/.gitignore | 0 acme/docs/_templates/.gitignore | 0 docs/.gitignore | 2 +- letsencrypt-apache/docs/.gitignore | 1 + letsencrypt-apache/docs/_static/.gitignore | 0 letsencrypt-apache/docs/_templates/.gitignore | 0 letsencrypt-compatibility-test/docs/.gitignore | 1 + letsencrypt-compatibility-test/docs/_static/.gitignore | 0 letsencrypt-compatibility-test/docs/_templates/.gitignore | 0 letsencrypt-nginx/docs/.gitignore | 1 + letsencrypt-nginx/docs/_static/.gitignore | 0 letsencrypt-nginx/docs/_templates/.gitignore | 0 letshelp-letsencrypt/docs/.gitignore | 1 + letshelp-letsencrypt/docs/_static/.gitignore | 0 letshelp-letsencrypt/docs/_templates/.gitignore | 0 16 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 acme/docs/.gitignore create mode 100644 acme/docs/_static/.gitignore create mode 100644 acme/docs/_templates/.gitignore create mode 100644 letsencrypt-apache/docs/.gitignore create mode 100644 letsencrypt-apache/docs/_static/.gitignore create mode 100644 letsencrypt-apache/docs/_templates/.gitignore create mode 100644 letsencrypt-compatibility-test/docs/.gitignore create mode 100644 letsencrypt-compatibility-test/docs/_static/.gitignore create mode 100644 letsencrypt-compatibility-test/docs/_templates/.gitignore create mode 100644 letsencrypt-nginx/docs/.gitignore create mode 100644 letsencrypt-nginx/docs/_static/.gitignore create mode 100644 letsencrypt-nginx/docs/_templates/.gitignore create mode 100644 letshelp-letsencrypt/docs/.gitignore create mode 100644 letshelp-letsencrypt/docs/_static/.gitignore create mode 100644 letshelp-letsencrypt/docs/_templates/.gitignore diff --git a/acme/docs/.gitignore b/acme/docs/.gitignore new file mode 100644 index 000000000..ba65b13af --- /dev/null +++ b/acme/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/acme/docs/_static/.gitignore b/acme/docs/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/acme/docs/_templates/.gitignore b/acme/docs/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/docs/.gitignore b/docs/.gitignore index 69fa449dd..ba65b13af 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1 @@ -_build/ +/_build/ diff --git a/letsencrypt-apache/docs/.gitignore b/letsencrypt-apache/docs/.gitignore new file mode 100644 index 000000000..ba65b13af --- /dev/null +++ b/letsencrypt-apache/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/letsencrypt-apache/docs/_static/.gitignore b/letsencrypt-apache/docs/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letsencrypt-apache/docs/_templates/.gitignore b/letsencrypt-apache/docs/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letsencrypt-compatibility-test/docs/.gitignore b/letsencrypt-compatibility-test/docs/.gitignore new file mode 100644 index 000000000..ba65b13af --- /dev/null +++ b/letsencrypt-compatibility-test/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/letsencrypt-compatibility-test/docs/_static/.gitignore b/letsencrypt-compatibility-test/docs/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letsencrypt-compatibility-test/docs/_templates/.gitignore b/letsencrypt-compatibility-test/docs/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letsencrypt-nginx/docs/.gitignore b/letsencrypt-nginx/docs/.gitignore new file mode 100644 index 000000000..ba65b13af --- /dev/null +++ b/letsencrypt-nginx/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/letsencrypt-nginx/docs/_static/.gitignore b/letsencrypt-nginx/docs/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letsencrypt-nginx/docs/_templates/.gitignore b/letsencrypt-nginx/docs/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letshelp-letsencrypt/docs/.gitignore b/letshelp-letsencrypt/docs/.gitignore new file mode 100644 index 000000000..ba65b13af --- /dev/null +++ b/letshelp-letsencrypt/docs/.gitignore @@ -0,0 +1 @@ +/_build/ diff --git a/letshelp-letsencrypt/docs/_static/.gitignore b/letshelp-letsencrypt/docs/_static/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/letshelp-letsencrypt/docs/_templates/.gitignore b/letshelp-letsencrypt/docs/_templates/.gitignore new file mode 100644 index 000000000..e69de29bb From 5fe5d69192c3ec21201fcdf63f87c6bdfdb418fa Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 13:45:18 +0000 Subject: [PATCH 036/216] Include docs in subpkgs tarballs --- acme/MANIFEST.in | 1 + letsencrypt-apache/MANIFEST.in | 1 + letsencrypt-compatibility-test/MANIFEST.in | 1 + letsencrypt-nginx/MANIFEST.in | 1 + letshelp-letsencrypt/MANIFEST.in | 1 + 5 files changed, 5 insertions(+) diff --git a/acme/MANIFEST.in b/acme/MANIFEST.in index ec2b09e05..a452d60d4 100644 --- a/acme/MANIFEST.in +++ b/acme/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE.txt include README.rst +recursive-include docs * recursive-include acme/testdata * diff --git a/letsencrypt-apache/MANIFEST.in b/letsencrypt-apache/MANIFEST.in index ff99bf0d8..51c2df858 100644 --- a/letsencrypt-apache/MANIFEST.in +++ b/letsencrypt-apache/MANIFEST.in @@ -1,4 +1,5 @@ include LICENSE.txt include README.rst +recursive-include docs * recursive-include letsencrypt_apache/tests/testdata * include letsencrypt_apache/options-ssl-apache.conf diff --git a/letsencrypt-compatibility-test/MANIFEST.in b/letsencrypt-compatibility-test/MANIFEST.in index 4d346a5d0..24d777841 100644 --- a/letsencrypt-compatibility-test/MANIFEST.in +++ b/letsencrypt-compatibility-test/MANIFEST.in @@ -1,5 +1,6 @@ include LICENSE.txt include README.rst +recursive-include docs * include letsencrypt_compatibility_test/configurators/apache/a2enmod.sh include letsencrypt_compatibility_test/configurators/apache/a2dismod.sh include letsencrypt_compatibility_test/configurators/apache/Dockerfile diff --git a/letsencrypt-nginx/MANIFEST.in b/letsencrypt-nginx/MANIFEST.in index c4bd67735..912d624d9 100644 --- a/letsencrypt-nginx/MANIFEST.in +++ b/letsencrypt-nginx/MANIFEST.in @@ -1,4 +1,5 @@ include LICENSE.txt include README.rst +recursive-include docs * recursive-include letsencrypt_nginx/tests/testdata * include letsencrypt_nginx/options-ssl-nginx.conf diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-letsencrypt/MANIFEST.in index 96c1d7ba5..6ea55a950 100644 --- a/letshelp-letsencrypt/MANIFEST.in +++ b/letshelp-letsencrypt/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE.txt include README.rst +recursive-include docs * recursive-include letshelp_letsencrypt/testdata * From 946ee632386d5973145d6371409173442947fbd0 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 13:49:26 +0000 Subject: [PATCH 037/216] docs_extra for subpkgs --- acme/setup.py | 7 +++++++ letsencrypt-apache/setup.py | 8 ++++++++ letsencrypt-compatibility-test/setup.py | 9 +++++++++ letsencrypt-nginx/setup.py | 8 ++++++++ letshelp-letsencrypt/setup.py | 8 ++++++++ 5 files changed, 40 insertions(+) diff --git a/acme/setup.py b/acme/setup.py index 6e27890b1..59c07d5c8 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -32,6 +32,12 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', + 'sphinxcontrib-programoutput', +] + testing_extras = [ 'nose', 'tox', @@ -61,6 +67,7 @@ setup( include_package_data=True, install_requires=install_requires, extras_require={ + 'docs': docs_extras, 'testing': testing_extras, }, entry_points={ diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 626e700b2..2180219f1 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -20,6 +20,11 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + setup( name='letsencrypt-apache', version=version, @@ -48,6 +53,9 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, entry_points={ 'letsencrypt.plugins': [ 'apache = letsencrypt_apache.configurator:ApacheConfigurator', diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 2e70fd1d7..3bd8f274e 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -19,6 +19,12 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +docs_extras = [ + 'repoze.sphinx.autointerface', + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + setup( name='letsencrypt-compatibility-test', version=version, @@ -41,6 +47,9 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, entry_points={ 'console_scripts': [ 'letsencrypt-compatibility-test = letsencrypt_compatibility_test.test_driver:main', diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index a37b8222b..5d80807d1 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -20,6 +20,11 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + setup( name='letsencrypt-nginx', version=version, @@ -48,6 +53,9 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, entry_points={ 'letsencrypt.plugins': [ 'nginx = letsencrypt_nginx.configurator:NginxConfigurator', diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index a83fc8843..a63e0aad4 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -14,6 +14,11 @@ if sys.version_info < (2, 7): else: install_requires.append('mock') +docs_extras = [ + 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags + 'sphinx_rtd_theme', +] + setup( name='letshelp-letsencrypt', version=version, @@ -41,6 +46,9 @@ setup( packages=find_packages(), include_package_data=True, install_requires=install_requires, + extras_require={ + 'docs': docs_extras, + }, entry_points={ 'console_scripts': [ 'letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main', From f922b9b6940523f56b18aaafbb1eeaeb030b2d9c Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 14:17:10 +0000 Subject: [PATCH 038/216] Split docs into subpkgs (fixes #969) --- acme/docs/api.rst | 8 ++++++++ {docs/pkgs/acme => acme/docs/api}/index.rst | 0 {docs/pkgs/acme => acme/docs/api}/jose.rst | 0 acme/docs/conf.py | 9 +++++++-- acme/docs/index.rst | 1 + {docs => acme/docs}/man/jws.rst | 0 docs/conf.py | 10 ---------- docs/contributing.rst | 10 ++-------- docs/index.rst | 1 - docs/pkgs.rst | 14 -------------- letsencrypt-apache/docs/api.rst | 8 ++++++++ .../docs/api/index.rst | 0 letsencrypt-apache/docs/conf.py | 12 +++++++++++- letsencrypt-apache/docs/index.rst | 5 +++++ letsencrypt-compatibility-test/docs/api.rst | 8 ++++++++ .../docs/api/index.rst | 0 letsencrypt-compatibility-test/docs/conf.py | 5 ++++- letsencrypt-compatibility-test/docs/index.rst | 5 +++++ letsencrypt-nginx/docs/api.rst | 8 ++++++++ .../docs/api/index.rst | 0 letsencrypt-nginx/docs/conf.py | 5 ++++- letsencrypt-nginx/docs/index.rst | 5 +++++ letshelp-letsencrypt/docs/api.rst | 8 ++++++++ .../docs/api/index.rst | 0 letshelp-letsencrypt/docs/conf.py | 5 ++++- letshelp-letsencrypt/docs/index.rst | 5 +++++ 26 files changed, 93 insertions(+), 39 deletions(-) create mode 100644 acme/docs/api.rst rename {docs/pkgs/acme => acme/docs/api}/index.rst (100%) rename {docs/pkgs/acme => acme/docs/api}/jose.rst (100%) rename {docs => acme/docs}/man/jws.rst (100%) delete mode 100644 docs/pkgs.rst create mode 100644 letsencrypt-apache/docs/api.rst rename docs/pkgs/letsencrypt_apache.rst => letsencrypt-apache/docs/api/index.rst (100%) create mode 100644 letsencrypt-compatibility-test/docs/api.rst rename docs/pkgs/letsencrypt_compatibility_test.rst => letsencrypt-compatibility-test/docs/api/index.rst (100%) create mode 100644 letsencrypt-nginx/docs/api.rst rename docs/pkgs/letsencrypt_nginx.rst => letsencrypt-nginx/docs/api/index.rst (100%) create mode 100644 letshelp-letsencrypt/docs/api.rst rename docs/pkgs/letshelp_letsencrypt.rst => letshelp-letsencrypt/docs/api/index.rst (100%) diff --git a/acme/docs/api.rst b/acme/docs/api.rst new file mode 100644 index 000000000..8668ec5d8 --- /dev/null +++ b/acme/docs/api.rst @@ -0,0 +1,8 @@ +================= +API Documentation +================= + +.. toctree:: + :glob: + + api/** diff --git a/docs/pkgs/acme/index.rst b/acme/docs/api/index.rst similarity index 100% rename from docs/pkgs/acme/index.rst rename to acme/docs/api/index.rst diff --git a/docs/pkgs/acme/jose.rst b/acme/docs/api/jose.rst similarity index 100% rename from docs/pkgs/acme/jose.rst rename to acme/docs/api/jose.rst diff --git a/acme/docs/conf.py b/acme/docs/conf.py index dc5dec0ff..1448aaea3 100644 --- a/acme/docs/conf.py +++ b/acme/docs/conf.py @@ -16,10 +16,14 @@ import sys import os import shlex + +here = os.path.abspath(os.path.dirname(__file__)) + + # 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. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) # -- General configuration ------------------------------------------------ @@ -271,7 +275,8 @@ latex_documents = [ # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'acme-python', u'acme-python Documentation', - [author], 1) + [author], 1), + ('man/jws', 'jws', u'jws script documentation', [project], 1), ] # If true, show URL addresses after external links. diff --git a/acme/docs/index.rst b/acme/docs/index.rst index 357d73fd0..940d79037 100644 --- a/acme/docs/index.rst +++ b/acme/docs/index.rst @@ -11,6 +11,7 @@ Contents: .. toctree:: :maxdepth: 2 + api Indices and tables diff --git a/docs/man/jws.rst b/acme/docs/man/jws.rst similarity index 100% rename from docs/man/jws.rst rename to acme/docs/man/jws.rst diff --git a/docs/conf.py b/docs/conf.py index 2de0dd877..dad4e608f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,13 +17,6 @@ import os import re import sys -import mock - - -# http://docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules -# c.f. #262 -sys.modules.update( - (mod_name, mock.MagicMock()) for mod_name in ['augeas']) here = os.path.abspath(os.path.dirname(__file__)) @@ -36,8 +29,6 @@ with codecs.open(init_fn, encoding='utf8') as fd: # 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. sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) -for pkg in 'acme', 'letsencrypt-apache', 'letsencrypt-nginx': - sys.path.insert(0, os.path.abspath(os.path.join(here, '..', pkg))) # -- General configuration ------------------------------------------------ @@ -292,7 +283,6 @@ man_pages = [ [project], 1), ('man/letsencrypt-renewer', 'letsencrypt-renewer', u'letsencrypt-renewer script documentation', [project], 1), - ('man/jws', 'jws', u'jws script documentation', [project], 1), ] # If true, show URL addresses after external links. diff --git a/docs/contributing.rst b/docs/contributing.rst index 6b6550c71..60e7f35c2 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -84,14 +84,8 @@ run (in a separate terminal):: If you would like to test `letsencrypt_nginx` plugin (highly encouraged) make sure to install prerequisites as listed in -``letsencrypt-nginx/tests/boulder-integration.sh``: - -.. include:: ../letsencrypt-nginx/tests/boulder-integration.sh - :start-line: 1 - :end-line: 2 - :code: shell - -and rerun the integration tests suite. +``letsencrypt-nginx/tests/boulder-integration.sh`` and rerun +the integration tests suite. .. _Boulder: https://github.com/letsencrypt/boulder .. _Go: https://golang.org diff --git a/docs/index.rst b/docs/index.rst index b076b45c6..72be096f9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,7 +13,6 @@ Welcome to the Let's Encrypt client documentation! :maxdepth: 1 api - pkgs Indices and tables diff --git a/docs/pkgs.rst b/docs/pkgs.rst deleted file mode 100644 index 2e1b18dfb..000000000 --- a/docs/pkgs.rst +++ /dev/null @@ -1,14 +0,0 @@ -======== -Packages -======== - -.. note:: It is planned to distribute `acme` and plugins separately as - described in `#358`_. For the time being those packages are bundled - together into a single repo, and single documentation. - -.. _`#358`: https://github.com/letsencrypt/letsencrypt/issues/358 - -.. toctree:: - :glob: - - pkgs/** diff --git a/letsencrypt-apache/docs/api.rst b/letsencrypt-apache/docs/api.rst new file mode 100644 index 000000000..8668ec5d8 --- /dev/null +++ b/letsencrypt-apache/docs/api.rst @@ -0,0 +1,8 @@ +================= +API Documentation +================= + +.. toctree:: + :glob: + + api/** diff --git a/docs/pkgs/letsencrypt_apache.rst b/letsencrypt-apache/docs/api/index.rst similarity index 100% rename from docs/pkgs/letsencrypt_apache.rst rename to letsencrypt-apache/docs/api/index.rst diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index f578531db..e439428af 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -16,10 +16,20 @@ import sys import os import shlex +import mock + + +# http://docs.readthedocs.org/en/latest/faq.html#i-get-import-errors-on-libraries-that-depend-on-c-modules +# c.f. #262 +sys.modules.update( + (mod_name, mock.MagicMock()) for mod_name in ['augeas']) + +here = os.path.abspath(os.path.dirname(__file__)) + # 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. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) # -- General configuration ------------------------------------------------ diff --git a/letsencrypt-apache/docs/index.rst b/letsencrypt-apache/docs/index.rst index 64f0e3e14..9be7c20d5 100644 --- a/letsencrypt-apache/docs/index.rst +++ b/letsencrypt-apache/docs/index.rst @@ -12,6 +12,11 @@ Contents: :maxdepth: 2 +.. toctree:: + :maxdepth: 1 + + api + Indices and tables ================== diff --git a/letsencrypt-compatibility-test/docs/api.rst b/letsencrypt-compatibility-test/docs/api.rst new file mode 100644 index 000000000..8668ec5d8 --- /dev/null +++ b/letsencrypt-compatibility-test/docs/api.rst @@ -0,0 +1,8 @@ +================= +API Documentation +================= + +.. toctree:: + :glob: + + api/** diff --git a/docs/pkgs/letsencrypt_compatibility_test.rst b/letsencrypt-compatibility-test/docs/api/index.rst similarity index 100% rename from docs/pkgs/letsencrypt_compatibility_test.rst rename to letsencrypt-compatibility-test/docs/api/index.rst diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 4ec0a1d9a..5a63c1dca 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -16,10 +16,13 @@ import sys import os import shlex + +here = os.path.abspath(os.path.dirname(__file__)) + # 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. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) # -- General configuration ------------------------------------------------ diff --git a/letsencrypt-compatibility-test/docs/index.rst b/letsencrypt-compatibility-test/docs/index.rst index 3df442c65..df57ee6e6 100644 --- a/letsencrypt-compatibility-test/docs/index.rst +++ b/letsencrypt-compatibility-test/docs/index.rst @@ -12,6 +12,11 @@ Contents: :maxdepth: 2 +.. toctree:: + :maxdepth: 1 + + api + Indices and tables ================== diff --git a/letsencrypt-nginx/docs/api.rst b/letsencrypt-nginx/docs/api.rst new file mode 100644 index 000000000..8668ec5d8 --- /dev/null +++ b/letsencrypt-nginx/docs/api.rst @@ -0,0 +1,8 @@ +================= +API Documentation +================= + +.. toctree:: + :glob: + + api/** diff --git a/docs/pkgs/letsencrypt_nginx.rst b/letsencrypt-nginx/docs/api/index.rst similarity index 100% rename from docs/pkgs/letsencrypt_nginx.rst rename to letsencrypt-nginx/docs/api/index.rst diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index e8e320542..8bcae3a78 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -16,10 +16,13 @@ import sys import os import shlex + +here = os.path.abspath(os.path.dirname(__file__)) + # 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. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) # -- General configuration ------------------------------------------------ diff --git a/letsencrypt-nginx/docs/index.rst b/letsencrypt-nginx/docs/index.rst index c4822af52..94db969ab 100644 --- a/letsencrypt-nginx/docs/index.rst +++ b/letsencrypt-nginx/docs/index.rst @@ -12,6 +12,11 @@ Contents: :maxdepth: 2 +.. toctree:: + :maxdepth: 1 + + api + Indices and tables ================== diff --git a/letshelp-letsencrypt/docs/api.rst b/letshelp-letsencrypt/docs/api.rst new file mode 100644 index 000000000..8668ec5d8 --- /dev/null +++ b/letshelp-letsencrypt/docs/api.rst @@ -0,0 +1,8 @@ +================= +API Documentation +================= + +.. toctree:: + :glob: + + api/** diff --git a/docs/pkgs/letshelp_letsencrypt.rst b/letshelp-letsencrypt/docs/api/index.rst similarity index 100% rename from docs/pkgs/letshelp_letsencrypt.rst rename to letshelp-letsencrypt/docs/api/index.rst diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index 3561a12d1..abbf3621d 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -16,10 +16,13 @@ import sys import os import shlex + +here = os.path.abspath(os.path.dirname(__file__)) + # 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. -#sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(0, os.path.abspath(os.path.join(here, '..'))) # -- General configuration ------------------------------------------------ diff --git a/letshelp-letsencrypt/docs/index.rst b/letshelp-letsencrypt/docs/index.rst index db806d874..6b67a2e1f 100644 --- a/letshelp-letsencrypt/docs/index.rst +++ b/letshelp-letsencrypt/docs/index.rst @@ -12,6 +12,11 @@ Contents: :maxdepth: 2 +.. toctree:: + :maxdepth: 1 + + api + Indices and tables ================== From fe49889b168321093ce7056e4314c751912f7e7d Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 15:42:14 +0000 Subject: [PATCH 039/216] Per subpkg requirements.txt for RTD --- acme/readthedocs.org.requirements.txt | 10 ++++++++++ letsencrypt-apache/readthedocs.org.requirements.txt | 10 ++++++++++ .../readthedocs.org.requirements.txt | 10 ++++++++++ letsencrypt-nginx/readthedocs.org.requirements.txt | 10 ++++++++++ letshelp-letsencrypt/readthedocs.org.requirements.txt | 10 ++++++++++ readthedocs.org.requirements.txt | 5 ----- 6 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 acme/readthedocs.org.requirements.txt create mode 100644 letsencrypt-apache/readthedocs.org.requirements.txt create mode 100644 letsencrypt-compatibility-test/readthedocs.org.requirements.txt create mode 100644 letsencrypt-nginx/readthedocs.org.requirements.txt create mode 100644 letshelp-letsencrypt/readthedocs.org.requirements.txt diff --git a/acme/readthedocs.org.requirements.txt b/acme/readthedocs.org.requirements.txt new file mode 100644 index 000000000..65e6c7cf3 --- /dev/null +++ b/acme/readthedocs.org.requirements.txt @@ -0,0 +1,10 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e acme[docs] diff --git a/letsencrypt-apache/readthedocs.org.requirements.txt b/letsencrypt-apache/readthedocs.org.requirements.txt new file mode 100644 index 000000000..9e782a01e --- /dev/null +++ b/letsencrypt-apache/readthedocs.org.requirements.txt @@ -0,0 +1,10 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e letsencrypt-apache[docs] diff --git a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt new file mode 100644 index 000000000..86d680426 --- /dev/null +++ b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt @@ -0,0 +1,10 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e letsencrypt-compatibility-test[docs] diff --git a/letsencrypt-nginx/readthedocs.org.requirements.txt b/letsencrypt-nginx/readthedocs.org.requirements.txt new file mode 100644 index 000000000..9a36ed259 --- /dev/null +++ b/letsencrypt-nginx/readthedocs.org.requirements.txt @@ -0,0 +1,10 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e letsencrypt-nginx[docs] diff --git a/letshelp-letsencrypt/readthedocs.org.requirements.txt b/letshelp-letsencrypt/readthedocs.org.requirements.txt new file mode 100644 index 000000000..898d2716e --- /dev/null +++ b/letshelp-letsencrypt/readthedocs.org.requirements.txt @@ -0,0 +1,10 @@ +# readthedocs.org gives no way to change the install command to "pip +# install -e .[docs]" (that would in turn install documentation +# dependencies), but it allows to specify a requirements.txt file at +# https://readthedocs.org/dashboard/letsencrypt/advanced/ (c.f. #259) + +# Although ReadTheDocs certainly doesn't need to install the project +# in --editable mode (-e), just "pip install .[docs]" does not work as +# expected and "pip install -e .[docs]" must be used instead + +-e letshelp-letsencrypt[docs] diff --git a/readthedocs.org.requirements.txt b/readthedocs.org.requirements.txt index 3c3a3c576..27cccb0a6 100644 --- a/readthedocs.org.requirements.txt +++ b/readthedocs.org.requirements.txt @@ -7,9 +7,4 @@ # in --editable mode (-e), just "pip install .[docs]" does not work as # expected and "pip install -e .[docs]" must be used instead --e acme -e .[docs] --e letsencrypt-apache --e letsencrypt-nginx --e letsencrypt-compatibility-test --e letshelp-letsencrypt From 5b757bdff235d127fd1ec0aa6c01fa85ef5c2a91 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 16:01:27 +0000 Subject: [PATCH 040/216] Split ACME API docs --- acme/docs/api/challenges.rst | 5 ++++ acme/docs/api/client.rst | 5 ++++ acme/docs/api/errors.rst | 5 ++++ acme/docs/api/fields.rst | 5 ++++ acme/docs/api/index.rst | 56 ------------------------------------ acme/docs/api/messages.rst | 5 ++++ acme/docs/api/other.rst | 5 ++++ acme/docs/api/standalone.rst | 5 ++++ acme/docs/index.rst | 3 ++ 9 files changed, 38 insertions(+), 56 deletions(-) create mode 100644 acme/docs/api/challenges.rst create mode 100644 acme/docs/api/client.rst create mode 100644 acme/docs/api/errors.rst create mode 100644 acme/docs/api/fields.rst delete mode 100644 acme/docs/api/index.rst create mode 100644 acme/docs/api/messages.rst create mode 100644 acme/docs/api/other.rst create mode 100644 acme/docs/api/standalone.rst diff --git a/acme/docs/api/challenges.rst b/acme/docs/api/challenges.rst new file mode 100644 index 000000000..7bc084fe9 --- /dev/null +++ b/acme/docs/api/challenges.rst @@ -0,0 +1,5 @@ +Challenges +---------- + +.. automodule:: acme.challenges + :members: diff --git a/acme/docs/api/client.rst b/acme/docs/api/client.rst new file mode 100644 index 000000000..d0ab89eb2 --- /dev/null +++ b/acme/docs/api/client.rst @@ -0,0 +1,5 @@ +Client +------ + +.. automodule:: acme.client + :members: diff --git a/acme/docs/api/errors.rst b/acme/docs/api/errors.rst new file mode 100644 index 000000000..644ff0e71 --- /dev/null +++ b/acme/docs/api/errors.rst @@ -0,0 +1,5 @@ +Errors +------ + +.. automodule:: acme.errors + :members: diff --git a/acme/docs/api/fields.rst b/acme/docs/api/fields.rst new file mode 100644 index 000000000..c224f7fef --- /dev/null +++ b/acme/docs/api/fields.rst @@ -0,0 +1,5 @@ +Fields +------ + +.. automodule:: acme.fields + :members: diff --git a/acme/docs/api/index.rst b/acme/docs/api/index.rst deleted file mode 100644 index 23c5a3284..000000000 --- a/acme/docs/api/index.rst +++ /dev/null @@ -1,56 +0,0 @@ -:mod:`acme` -=========== - -.. contents:: - -.. automodule:: acme - :members: - - -Client ------- - -.. automodule:: acme.client - :members: - - -Messages --------- - -.. automodule:: acme.messages - :members: - - -Challenges ----------- - -.. automodule:: acme.challenges - :members: - - -Other ACME objects ------------------- - -.. automodule:: acme.other - :members: - - -Fields ------- - -.. automodule:: acme.fields - :members: - - -Errors ------- - -.. automodule:: acme.errors - :members: - - -Standalone ----------- - -.. automodule:: acme.standalone - :members: diff --git a/acme/docs/api/messages.rst b/acme/docs/api/messages.rst new file mode 100644 index 000000000..0374a72c8 --- /dev/null +++ b/acme/docs/api/messages.rst @@ -0,0 +1,5 @@ +Messages +-------- + +.. automodule:: acme.messages + :members: diff --git a/acme/docs/api/other.rst b/acme/docs/api/other.rst new file mode 100644 index 000000000..eb27a5d53 --- /dev/null +++ b/acme/docs/api/other.rst @@ -0,0 +1,5 @@ +Other ACME objects +------------------ + +.. automodule:: acme.other + :members: diff --git a/acme/docs/api/standalone.rst b/acme/docs/api/standalone.rst new file mode 100644 index 000000000..a65e5df2a --- /dev/null +++ b/acme/docs/api/standalone.rst @@ -0,0 +1,5 @@ +Standalone +---------- + +.. automodule:: acme.standalone + :members: diff --git a/acme/docs/index.rst b/acme/docs/index.rst index 940d79037..8d298054e 100644 --- a/acme/docs/index.rst +++ b/acme/docs/index.rst @@ -13,6 +13,9 @@ Contents: api +.. automodule:: acme + :members: + Indices and tables ================== From ca464c25fbe15d197938142af690ba6c0d32e379 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 16:10:21 +0000 Subject: [PATCH 041/216] Split JOSE API docs --- acme/docs/api.rst | 2 +- acme/docs/api/jose.rst | 67 +++---------------------------- acme/docs/api/jose/base64.rst | 5 +++ acme/docs/api/jose/errors.rst | 5 +++ acme/docs/api/jose/interfaces.rst | 5 +++ acme/docs/api/jose/json_util.rst | 5 +++ acme/docs/api/jose/jwa.rst | 5 +++ acme/docs/api/jose/jwk.rst | 5 +++ acme/docs/api/jose/jws.rst | 5 +++ acme/docs/api/jose/util.rst | 5 +++ 10 files changed, 46 insertions(+), 63 deletions(-) create mode 100644 acme/docs/api/jose/base64.rst create mode 100644 acme/docs/api/jose/errors.rst create mode 100644 acme/docs/api/jose/interfaces.rst create mode 100644 acme/docs/api/jose/json_util.rst create mode 100644 acme/docs/api/jose/jwa.rst create mode 100644 acme/docs/api/jose/jwk.rst create mode 100644 acme/docs/api/jose/jws.rst create mode 100644 acme/docs/api/jose/util.rst diff --git a/acme/docs/api.rst b/acme/docs/api.rst index 8668ec5d8..c874d8470 100644 --- a/acme/docs/api.rst +++ b/acme/docs/api.rst @@ -5,4 +5,4 @@ API Documentation .. toctree:: :glob: - api/** + api/* diff --git a/acme/docs/api/jose.rst b/acme/docs/api/jose.rst index fa3a0e9bb..0f7dd627c 100644 --- a/acme/docs/api/jose.rst +++ b/acme/docs/api/jose.rst @@ -1,67 +1,10 @@ -:mod:`acme.jose` -================ - -.. contents:: +JOSE +---- .. automodule:: acme.jose :members: +.. toctree:: + :glob: -JSON Web Algorithms -------------------- - -.. automodule:: acme.jose.jwa - :members: - - -JSON Web Key ------------- - -.. automodule:: acme.jose.jwk - :members: - - -JSON Web Signature ------------------- - -.. automodule:: acme.jose.jws - :members: - - -Implementation details ----------------------- - - -Interfaces -~~~~~~~~~~ - -.. automodule:: acme.jose.interfaces - :members: - - -Errors -~~~~~~ - -.. automodule:: acme.jose.errors - :members: - - -JSON utilities -~~~~~~~~~~~~~~ - -.. automodule:: acme.jose.json_util - :members: - - -JOSE Base64 -~~~~~~~~~~~ - -.. automodule:: acme.jose.b64 - :members: - - -Utilities -~~~~~~~~~ - -.. automodule:: acme.jose.util - :members: + jose/* diff --git a/acme/docs/api/jose/base64.rst b/acme/docs/api/jose/base64.rst new file mode 100644 index 000000000..a40f8b11b --- /dev/null +++ b/acme/docs/api/jose/base64.rst @@ -0,0 +1,5 @@ +JOSE Base64 +----------- + +.. automodule:: acme.jose.b64 + :members: diff --git a/acme/docs/api/jose/errors.rst b/acme/docs/api/jose/errors.rst new file mode 100644 index 000000000..60d9b5c78 --- /dev/null +++ b/acme/docs/api/jose/errors.rst @@ -0,0 +1,5 @@ +Errors +------ + +.. automodule:: acme.jose.errors + :members: diff --git a/acme/docs/api/jose/interfaces.rst b/acme/docs/api/jose/interfaces.rst new file mode 100644 index 000000000..7b72b364f --- /dev/null +++ b/acme/docs/api/jose/interfaces.rst @@ -0,0 +1,5 @@ +Interfaces +---------- + +.. automodule:: acme.jose.interfaces + :members: diff --git a/acme/docs/api/jose/json_util.rst b/acme/docs/api/jose/json_util.rst new file mode 100644 index 000000000..f06edead2 --- /dev/null +++ b/acme/docs/api/jose/json_util.rst @@ -0,0 +1,5 @@ +JSON utilities +-------------- + +.. automodule:: acme.jose.json_util + :members: diff --git a/acme/docs/api/jose/jwa.rst b/acme/docs/api/jose/jwa.rst new file mode 100644 index 000000000..c858a5d1d --- /dev/null +++ b/acme/docs/api/jose/jwa.rst @@ -0,0 +1,5 @@ +JSON Web Algorithms +------------------- + +.. automodule:: acme.jose.jwa + :members: diff --git a/acme/docs/api/jose/jwk.rst b/acme/docs/api/jose/jwk.rst new file mode 100644 index 000000000..8e6bbe13f --- /dev/null +++ b/acme/docs/api/jose/jwk.rst @@ -0,0 +1,5 @@ +JSON Web Key +------------ + +.. automodule:: acme.jose.jwk + :members: diff --git a/acme/docs/api/jose/jws.rst b/acme/docs/api/jose/jws.rst new file mode 100644 index 000000000..15335bf8f --- /dev/null +++ b/acme/docs/api/jose/jws.rst @@ -0,0 +1,5 @@ +JSON Web Signature +------------------ + +.. automodule:: acme.jose.jws + :members: diff --git a/acme/docs/api/jose/util.rst b/acme/docs/api/jose/util.rst new file mode 100644 index 000000000..41cbbcc31 --- /dev/null +++ b/acme/docs/api/jose/util.rst @@ -0,0 +1,5 @@ +Utilities +--------- + +.. automodule:: acme.jose.util + :members: From 762e9e578f3f31760e151c00c8d2dff9510ba483 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 16:14:42 +0000 Subject: [PATCH 042/216] Split letsencrypt-apache API docs --- .../docs/api/augeas_configurator.rst | 5 +++ letsencrypt-apache/docs/api/configurator.rst | 5 +++ letsencrypt-apache/docs/api/display_ops.rst | 5 +++ letsencrypt-apache/docs/api/dvsni.rst | 5 +++ letsencrypt-apache/docs/api/index.rst | 42 ------------------- letsencrypt-apache/docs/api/obj.rst | 5 +++ letsencrypt-apache/docs/api/parser.rst | 5 +++ letsencrypt-apache/docs/index.rst | 4 ++ 8 files changed, 34 insertions(+), 42 deletions(-) create mode 100644 letsencrypt-apache/docs/api/augeas_configurator.rst create mode 100644 letsencrypt-apache/docs/api/configurator.rst create mode 100644 letsencrypt-apache/docs/api/display_ops.rst create mode 100644 letsencrypt-apache/docs/api/dvsni.rst delete mode 100644 letsencrypt-apache/docs/api/index.rst create mode 100644 letsencrypt-apache/docs/api/obj.rst create mode 100644 letsencrypt-apache/docs/api/parser.rst diff --git a/letsencrypt-apache/docs/api/augeas_configurator.rst b/letsencrypt-apache/docs/api/augeas_configurator.rst new file mode 100644 index 000000000..3b1821e3d --- /dev/null +++ b/letsencrypt-apache/docs/api/augeas_configurator.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_apache.augeas_configurator` +--------------------------------------------- + +.. automodule:: letsencrypt_apache.augeas_configurator + :members: diff --git a/letsencrypt-apache/docs/api/configurator.rst b/letsencrypt-apache/docs/api/configurator.rst new file mode 100644 index 000000000..2ed613286 --- /dev/null +++ b/letsencrypt-apache/docs/api/configurator.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_apache.configurator` +-------------------------------------- + +.. automodule:: letsencrypt_apache.configurator + :members: diff --git a/letsencrypt-apache/docs/api/display_ops.rst b/letsencrypt-apache/docs/api/display_ops.rst new file mode 100644 index 000000000..59ff9d15e --- /dev/null +++ b/letsencrypt-apache/docs/api/display_ops.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_apache.display_ops` +------------------------------------- + +.. automodule:: letsencrypt_apache.display_ops + :members: diff --git a/letsencrypt-apache/docs/api/dvsni.rst b/letsencrypt-apache/docs/api/dvsni.rst new file mode 100644 index 000000000..945771db8 --- /dev/null +++ b/letsencrypt-apache/docs/api/dvsni.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_apache.dvsni` +------------------------------- + +.. automodule:: letsencrypt_apache.dvsni + :members: diff --git a/letsencrypt-apache/docs/api/index.rst b/letsencrypt-apache/docs/api/index.rst deleted file mode 100644 index c4e7e8e6e..000000000 --- a/letsencrypt-apache/docs/api/index.rst +++ /dev/null @@ -1,42 +0,0 @@ -:mod:`letsencrypt_apache` -------------------------- - -.. automodule:: letsencrypt_apache - :members: - -:mod:`letsencrypt_apache.configurator` -====================================== - -.. automodule:: letsencrypt_apache.configurator - :members: - -:mod:`letsencrypt_apache.display_ops` -===================================== - -.. automodule:: letsencrypt_apache.display_ops - :members: - -:mod:`letsencrypt_apache.dvsni` -=============================== - -.. automodule:: letsencrypt_apache.dvsni - :members: - -:mod:`letsencrypt_apache.obj` -============================= - -.. automodule:: letsencrypt_apache.obj - :members: - -:mod:`letsencrypt_apache.parser` -================================ - -.. automodule:: letsencrypt_apache.parser - :members: - - -:mod:`letsencrypt_apache.augeas_configurator` -============================================= - -.. automodule:: letsencrypt_apache.augeas_configurator - :members: diff --git a/letsencrypt-apache/docs/api/obj.rst b/letsencrypt-apache/docs/api/obj.rst new file mode 100644 index 000000000..969293ca1 --- /dev/null +++ b/letsencrypt-apache/docs/api/obj.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_apache.obj` +----------------------------- + +.. automodule:: letsencrypt_apache.obj + :members: diff --git a/letsencrypt-apache/docs/api/parser.rst b/letsencrypt-apache/docs/api/parser.rst new file mode 100644 index 000000000..0c998e06c --- /dev/null +++ b/letsencrypt-apache/docs/api/parser.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_apache.parser` +-------------------------------- + +.. automodule:: letsencrypt_apache.parser + :members: diff --git a/letsencrypt-apache/docs/index.rst b/letsencrypt-apache/docs/index.rst index 9be7c20d5..f968ccbef 100644 --- a/letsencrypt-apache/docs/index.rst +++ b/letsencrypt-apache/docs/index.rst @@ -18,6 +18,10 @@ Contents: api +.. automodule:: letsencrypt_apache + :members: + + Indices and tables ================== From a1847362d530b6d0257ef7939a89cac5ec46c578 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 18 Oct 2015 16:17:11 +0000 Subject: [PATCH 043/216] Split letsencrypt-nginx API docs --- letsencrypt-nginx/docs/api/dvsni.rst | 5 ++++ letsencrypt-nginx/docs/api/index.rst | 35 ---------------------- letsencrypt-nginx/docs/api/nginxparser.rst | 5 ++++ letsencrypt-nginx/docs/api/obj.rst | 5 ++++ letsencrypt-nginx/docs/api/parser.rst | 5 ++++ letsencrypt-nginx/docs/index.rst | 4 +++ 6 files changed, 24 insertions(+), 35 deletions(-) create mode 100644 letsencrypt-nginx/docs/api/dvsni.rst delete mode 100644 letsencrypt-nginx/docs/api/index.rst create mode 100644 letsencrypt-nginx/docs/api/nginxparser.rst create mode 100644 letsencrypt-nginx/docs/api/obj.rst create mode 100644 letsencrypt-nginx/docs/api/parser.rst diff --git a/letsencrypt-nginx/docs/api/dvsni.rst b/letsencrypt-nginx/docs/api/dvsni.rst new file mode 100644 index 000000000..4f5f9d7e3 --- /dev/null +++ b/letsencrypt-nginx/docs/api/dvsni.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_nginx.dvsni` +------------------------------ + +.. automodule:: letsencrypt_nginx.dvsni + :members: diff --git a/letsencrypt-nginx/docs/api/index.rst b/letsencrypt-nginx/docs/api/index.rst deleted file mode 100644 index 03114b685..000000000 --- a/letsencrypt-nginx/docs/api/index.rst +++ /dev/null @@ -1,35 +0,0 @@ -:mod:`letsencrypt_nginx` ------------------------- - -.. automodule:: letsencrypt_nginx - :members: - -:mod:`letsencrypt_nginx.configurator` -===================================== - -.. automodule:: letsencrypt_nginx.configurator - :members: - -:mod:`letsencrypt_nginx.dvsni` -============================== - -.. automodule:: letsencrypt_nginx.dvsni - :members: - -:mod:`letsencrypt_nginx.obj` -============================ - -.. automodule:: letsencrypt_nginx.obj - :members: - -:mod:`letsencrypt_nginx.parser` -=============================== - -.. automodule:: letsencrypt_nginx.parser - :members: - -:mod:`letsencrypt_nginx.nginxparser` -==================================== - -.. automodule:: letsencrypt_nginx.nginxparser - :members: diff --git a/letsencrypt-nginx/docs/api/nginxparser.rst b/letsencrypt-nginx/docs/api/nginxparser.rst new file mode 100644 index 000000000..e55bda0b1 --- /dev/null +++ b/letsencrypt-nginx/docs/api/nginxparser.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_nginx.nginxparser` +------------------------------------ + +.. automodule:: letsencrypt_nginx.nginxparser + :members: diff --git a/letsencrypt-nginx/docs/api/obj.rst b/letsencrypt-nginx/docs/api/obj.rst new file mode 100644 index 000000000..418b87cf7 --- /dev/null +++ b/letsencrypt-nginx/docs/api/obj.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_nginx.obj` +---------------------------- + +.. automodule:: letsencrypt_nginx.obj + :members: diff --git a/letsencrypt-nginx/docs/api/parser.rst b/letsencrypt-nginx/docs/api/parser.rst new file mode 100644 index 000000000..6582263ef --- /dev/null +++ b/letsencrypt-nginx/docs/api/parser.rst @@ -0,0 +1,5 @@ +:mod:`letsencrypt_nginx.parser` +------------------------------- + +.. automodule:: letsencrypt_nginx.parser + :members: diff --git a/letsencrypt-nginx/docs/index.rst b/letsencrypt-nginx/docs/index.rst index 94db969ab..e4f8f715f 100644 --- a/letsencrypt-nginx/docs/index.rst +++ b/letsencrypt-nginx/docs/index.rst @@ -18,6 +18,10 @@ Contents: api +.. automodule:: letsencrypt_nginx + :members: + + Indices and tables ================== From 1964bdeb78d69e7de7b36f58c0b39c2655037d37 Mon Sep 17 00:00:00 2001 From: Harlan Lieberman-Berg Date: Wed, 21 Oct 2015 19:36:07 -0400 Subject: [PATCH 044/216] Correct minor syntax error in docs/conf.py. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index dad4e608f..124f0f9ad 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -314,6 +314,6 @@ texinfo_documents = [ intersphinx_mapping = { - 'python': ('https://docs.python.org/': None), + 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org', None), } From ccfeeb9fe534823ea8566702bd00bcdea0faf8ba Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 21:00:30 -0700 Subject: [PATCH 045/216] Fix test cases; along the way, have --csr save fullchain.pem --- letsencrypt/cli.py | 32 ++++++++++++++++++++------------ letsencrypt/client.py | 23 +++++++++++++++++------ letsencrypt/tests/cli_test.py | 18 ++++++++++-------- letsencrypt/tests/client_test.py | 8 ++++++-- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 31891dd5d..3b5339a1a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -267,21 +267,29 @@ def _treat_as_renewal(config, domains): return None -def _report_new_cert(lineage): +def _report_new_cert(cert_path, fullchain_path): """ Reports the creation of a new certificate to the user. - :param RenewableCert lineage: the lineage of the new cert + :param string cert_path: path to cert + :param string fullchain_path: path to full chain """ - expiry = crypto_util.notAfter(lineage.cert).date() + expiry = crypto_util.notAfter(cert_path).date() reporter_util = zope.component.getUtility(interfaces.IReporter) - # Tell the user about fullchain.pem because that's what modern webservers - # (Nginx and Apache2.4) will want. + if fullchain_path: + # Print the path to fullchain.pem because that's what modern webservers + # (Nginx and Apache2.4) will want. + and_chain = "and chain have" + path = fullchain_path + else: + # Unless we're in .csr mode and there really isn't one + and_chain = "has " + path = cert_path # XXX Perhaps one day we could detect the presence of known old webservers # and say something more informative here. - msg = ("Congratulations! Your certificate and chain have been saved at {0}." - " Your cert will expire on {1}. To obtain a new version of the " + msg = ("Congratulations! Your certificate {0} been saved at {1}." + " Your cert will expire on {2}. To obtain a new version of the " "certificate in the future, simply run Let's Encrypt again." - .format(lineage.fullchain, expiry)) + .format(and_chain, path, expiry)) reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) @@ -310,7 +318,7 @@ def _auth_from_domains(le_client, config, domains, plugins): if not lineage: raise Error("Certificate could not be obtained") - _report_new_cert(lineage) + _report_new_cert(lineage.cert, lineage.fullchain) return lineage @@ -461,9 +469,9 @@ def auth(args, config, plugins): if args.csr is not None: certr, chain = le_client.obtain_certificate_from_csr(le_util.CSR( file=args.csr[0], data=args.csr[1], form="der")) - cert_path, _ = le_client.save_certificate( - certr, chain, args.cert_path, args.chain_path) - _report_new_cert(cert_path) + cert_path, _, cert_fullchain = le_client.save_certificate( + certr, chain, args.cert_path, args.chain_path, args.fullchain_path) + _report_new_cert(cert_path, cert_fullchain) else: domains = _find_domains(args, installer) _auth_from_domains(le_client, config, domains, plugins) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 732bdcf03..a7a4847b2 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -258,7 +258,7 @@ class Client(object): params, config, cli_config) return lineage - def save_certificate(self, certr, chain_cert, cert_path, chain_path): + def save_certificate(self, certr, chain_cert, cert_path, chain_path, fullchain_path): # pylint: disable=no-self-use """Saves the certificate received from the ACME server. @@ -268,20 +268,22 @@ class Client(object): :param list chain_cert: :param str cert_path: Candidate path to a certificate. :param str chain_path: Candidate path to a certificate chain. + :param str fullchain_path: Candidate path to a full cert chain. - :returns: cert_path, chain_path (absolute paths to the actual files) + :returns: cert_path, chain_path, fullchain_path (absolute paths to the actual files) :rtype: `tuple` of `str` :raises IOError: If unable to find room to write the cert files """ - for path in cert_path, chain_path: + for path in cert_path, chain_path, fullchain_path: le_util.make_or_verify_dir( os.path.dirname(path), 0o755, os.geteuid(), self.config.strict_permissions) # try finally close cert_chain_abspath = None + fullchain_abspath = None cert_file, act_cert_path = le_util.unique_file(cert_path, 0o644) # TODO: Except cert_pem = OpenSSL.crypto.dump_certificate( @@ -294,8 +296,7 @@ class Client(object): act_cert_path) if chain_cert: - chain_file, act_chain_path = le_util.unique_file( - chain_path, 0o644) + chain_file, act_chain_path = le_util.unique_file(chain_path, 0o644) # TODO: Except chain_pem = crypto_util.dump_pyopenssl_chain(chain_cert) try: @@ -308,7 +309,17 @@ class Client(object): # This expects a valid chain file cert_chain_abspath = os.path.abspath(act_chain_path) - return os.path.abspath(act_cert_path), cert_chain_abspath + # fullchain is cert + chain + fullchain_file, act_fullchain_path = le_util.unique_file( + fullchain_path, 0o644) + try: + fullchain_file.write(cert_pem + chain_pem) + finally: + fullchain_file.close() + logger.info("Cert chain written to %s", act_fullchain_path) + fullchain_abspath = os.path.abspath(act_fullchain_path) + + return os.path.abspath(act_cert_path), cert_chain_abspath, fullchain_abspath def deploy_certificate(self, domains, privkey_path, cert_path, chain_path, fullchain_path): diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 9d9164f24..73ab84bdf 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -149,7 +149,7 @@ class CLITest(unittest.TestCase): date = '1970-01-01' mock_notAfter().date.return_value = date - mock_lineage = mock.MagicMock(cert=cert_path) + mock_lineage = mock.MagicMock(cert=cert_path, fullchain=cert_path) mock_client = mock.MagicMock() mock_client.obtain_and_enroll_certificate.return_value = mock_lineage self._auth_new_request_common(mock_client) @@ -177,9 +177,10 @@ class CLITest(unittest.TestCase): @mock.patch('letsencrypt.cli._treat_as_renewal') @mock.patch('letsencrypt.cli._init_le_client') def test_auth_renewal(self, mock_init, mock_renewal, mock_get_utility): - cert_path = '/etc/letsencrypt/live/foo.bar' + cert_path = '/etc/letsencrypt/live/foo.bar/cert.pem' + chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' - mock_lineage = mock.MagicMock(cert=cert_path) + mock_lineage = mock.MagicMock(cert=cert_path,fullchain=chain_path) mock_cert = mock.MagicMock(body='body') mock_key = mock.MagicMock(pem='pem_key') mock_renewal.return_value = mock_lineage @@ -195,7 +196,7 @@ class CLITest(unittest.TestCase): mock_lineage.update_all_links_to.assert_called_once_with( mock_lineage.latest_common_version()) self.assertTrue( - cert_path in mock_get_utility().add_message.call_args[0][0]) + chain_path in mock_get_utility().add_message.call_args[0][0]) @mock.patch('letsencrypt.crypto_util.notAfter') @mock.patch('letsencrypt.cli.display_ops.pick_installer') @@ -203,23 +204,24 @@ class CLITest(unittest.TestCase): @mock.patch('letsencrypt.cli._init_le_client') def test_auth_csr(self, mock_init, mock_get_utility, mock_pick_installer, mock_notAfter): - cert_path = '/etc/letsencrypt/live/foo.bar' + cert_path = '/etc/letsencrypt/live/blahcert.pem' date = '1970-01-01' mock_notAfter().date.return_value = date mock_client = mock.MagicMock() mock_client.obtain_certificate_from_csr.return_value = ('certr', 'chain') - mock_client.save_certificate.return_value = cert_path, None + mock_client.save_certificate.return_value = cert_path, None, None mock_init.return_value = mock_client installer = 'installer' self._call( ['-a', 'standalone', '-i', installer, 'auth', '--csr', CSR, - '--cert-path', cert_path, '--chain-path', '/']) + '--cert-path', cert_path, '--fullchain-path', '/', + '--chain-path', '/']) self.assertEqual(mock_pick_installer.call_args[0][1], installer) mock_client.save_certificate.assert_called_once_with( - 'certr', 'chain', cert_path, '/') + 'certr', 'chain', cert_path, '/', '/') self.assertTrue( cert_path in mock_get_utility().add_message.call_args[0][0]) self.assertTrue( diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 3f7b84a64..249778964 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -124,14 +124,18 @@ class ClientTest(unittest.TestCase): cert2 = test_util.load_cert(certs[2]) candidate_cert_path = os.path.join(tmp_path, "certs", "cert.pem") candidate_chain_path = os.path.join(tmp_path, "chains", "chain.pem") + candidate_fullchain_path = os.path.join(tmp_path, "chains", "fullchain.pem") - cert_path, chain_path = self.client.save_certificate( - certr, [cert1, cert2], candidate_cert_path, candidate_chain_path) + cert_path, chain_path, fullchain_path = self.client.save_certificate( + certr, [cert1, cert2], candidate_cert_path, candidate_chain_path, + candidate_fullchain_path) self.assertEqual(os.path.dirname(cert_path), os.path.dirname(candidate_cert_path)) self.assertEqual(os.path.dirname(chain_path), os.path.dirname(candidate_chain_path)) + self.assertEqual(os.path.dirname(fullchain_path), + os.path.dirname(candidate_fullchain_path)) with open(cert_path, "r") as cert_file: cert_contents = cert_file.read() From cf5cb9d3ff139da5090891e43f1b9cf3808f9c5e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 21:15:19 -0700 Subject: [PATCH 046/216] Satisfy pylint... sort of --- letsencrypt/client.py | 5 ++++- letsencrypt/tests/cli_test.py | 2 +- letsencrypt/tests/client_test.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index a7a4847b2..31597c7d3 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -259,7 +259,7 @@ class Client(object): return lineage def save_certificate(self, certr, chain_cert, cert_path, chain_path, fullchain_path): - # pylint: disable=no-self-use + # pylint: disable=no-self-use,too-many-locals """Saves the certificate received from the ACME server. :param certr: ACME "certificate" resource. @@ -295,6 +295,9 @@ class Client(object): logger.info("Server issued certificate; certificate written to %s", act_cert_path) + # TODO too long, refactor... either split this into another function + # up one level (though it needs a copy of cert_pem) or find a way to + # reuse machinery from storage.py if chain_cert: chain_file, act_chain_path = le_util.unique_file(chain_path, 0o644) # TODO: Except diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 73ab84bdf..8e9172055 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -180,7 +180,7 @@ class CLITest(unittest.TestCase): cert_path = '/etc/letsencrypt/live/foo.bar/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' - mock_lineage = mock.MagicMock(cert=cert_path,fullchain=chain_path) + mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path) mock_cert = mock.MagicMock(body='body') mock_key = mock.MagicMock(pem='pem_key') mock_renewal.return_value = mock_lineage diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 249778964..13613374f 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -114,7 +114,7 @@ class ClientTest(unittest.TestCase): mock.sentinel.key, domains, self.config.csr_dir) self._check_obtain_certificate() - def test_save_certificate(self): + def test_save_certificate(self): # pylint: disable=too-many-locals certs = ["matching_cert.pem", "cert.pem", "cert-san.pem"] tmp_path = tempfile.mkdtemp() os.chmod(tmp_path, 0o755) # TODO: really?? From 8a09e616c2e056f772c3e96ed2bd1d63e7519da7 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 22:58:18 -0700 Subject: [PATCH 047/216] its it's --- README.rst | 2 +- letsencrypt-auto | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 4040ca284..769bc7a8f 100644 --- a/README.rst +++ b/README.rst @@ -80,7 +80,7 @@ Current Features - apache/2.x (tested and working on Ubuntu Linux) - nginx/0.8.48+ (under development) - - standalone (runs it's own simple webserver to prove you control a domain) + - standalone (runs its own simple webserver to prove you control a domain) * The private key is generated locally on your system. * Can talk to the Let's Encrypt (demo) CA or optionally to other ACME diff --git a/letsencrypt-auto b/letsencrypt-auto index e7c9d6737..5b974c1f8 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -1,5 +1,8 @@ #!/bin/sh -e # +# A script to run the latest release version of the Let's Encrypt in a +# virtual environment +# # Installs and updates the letencrypt virtualenv, and runs letsencrypt # using that virtual environment. This allows the client to function decently # without requiring specific versions of its dependencies from the operating From 1c215d0c671fb301341045456b8e709ea95fe509 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 21 Oct 2015 23:10:55 -0700 Subject: [PATCH 048/216] Sphinxify also pythonic str instead of string --- letsencrypt/cli.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 3b5339a1a..3ae58d12a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -270,8 +270,9 @@ def _treat_as_renewal(config, domains): def _report_new_cert(cert_path, fullchain_path): """ Reports the creation of a new certificate to the user. - :param string cert_path: path to cert - :param string fullchain_path: path to full chain + + :param str cert_path: path to cert + :param str fullchain_path: path to full chain """ expiry = crypto_util.notAfter(cert_path).date() reporter_util = zope.component.getUtility(interfaces.IReporter) @@ -326,8 +327,8 @@ def _auth_from_domains(le_client, config, domains, plugins): def set_configurator(previously, now): """ Setting configurators multiple ways is okay, as long as they all agree - :param string previously: previously identified request for the installer/authenticator - :param string requested: the request currently being processed + :param str previously: previously identified request for the installer/authenticator + :param str requested: the request currently being processed """ if now is None: # we're not actually setting anything @@ -343,8 +344,8 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): """ Raise the most helpful error message about a plugin being unavailable - :param string cfg_type: either "installer" or "authenticator" - :param string requested: the plugin that was requested + :param str cfg_type: either "installer" or "authenticator" + :param str requested: the plugin that was requested :param PluginRegistry plugins: available plugins :raises error.PluginSelectionError: if there was a problem From e4a0eee66296aaa07ff6c8b5c3703cca0133f4fa Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 21 Oct 2015 23:25:05 -0700 Subject: [PATCH 049/216] Satisfied spacing OCD --- letsencrypt/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 3ae58d12a..5163f2868 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -268,11 +268,11 @@ def _treat_as_renewal(config, domains): def _report_new_cert(cert_path, fullchain_path): - """ - Reports the creation of a new certificate to the user. + """Reports the creation of a new certificate to the user. :param str cert_path: path to cert :param str fullchain_path: path to full chain + """ expiry = crypto_util.notAfter(cert_path).date() reporter_util = zope.component.getUtility(interfaces.IReporter) From f13566e5ba29edfad9879bc5eacb1e78eb435ec7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 22 Oct 2015 00:35:52 -0700 Subject: [PATCH 050/216] Quick refactor --- letsencrypt/client.py | 64 ++++++++++++++++---------------- letsencrypt/tests/client_test.py | 8 ++-- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 31597c7d3..3a6d90472 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -258,8 +258,8 @@ class Client(object): params, config, cli_config) return lineage - def save_certificate(self, certr, chain_cert, cert_path, chain_path, fullchain_path): - # pylint: disable=no-self-use,too-many-locals + def save_certificate(self, certr, chain_cert, + cert_path, chain_path, fullchain_path): """Saves the certificate received from the ACME server. :param certr: ACME "certificate" resource. @@ -270,7 +270,8 @@ class Client(object): :param str chain_path: Candidate path to a certificate chain. :param str fullchain_path: Candidate path to a full cert chain. - :returns: cert_path, chain_path, fullchain_path (absolute paths to the actual files) + :returns: cert_path, chain_path, and fullchain_path as absolute + paths to the actual files :rtype: `tuple` of `str` :raises IOError: If unable to find room to write the cert files @@ -281,13 +282,9 @@ class Client(object): os.path.dirname(path), 0o755, os.geteuid(), self.config.strict_permissions) - # try finally close - cert_chain_abspath = None - fullchain_abspath = None - cert_file, act_cert_path = le_util.unique_file(cert_path, 0o644) - # TODO: Except cert_pem = OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, certr.body) + cert_file, act_cert_path = le_util.unique_file(cert_path, 0o644) try: cert_file.write(cert_pem) finally: @@ -295,32 +292,13 @@ class Client(object): logger.info("Server issued certificate; certificate written to %s", act_cert_path) - # TODO too long, refactor... either split this into another function - # up one level (though it needs a copy of cert_pem) or find a way to - # reuse machinery from storage.py + cert_chain_abspath = None + fullchain_abspath = None if chain_cert: - chain_file, act_chain_path = le_util.unique_file(chain_path, 0o644) - # TODO: Except chain_pem = crypto_util.dump_pyopenssl_chain(chain_cert) - try: - chain_file.write(chain_pem) - finally: - chain_file.close() - - logger.info("Cert chain written to %s", act_chain_path) - - # This expects a valid chain file - cert_chain_abspath = os.path.abspath(act_chain_path) - - # fullchain is cert + chain - fullchain_file, act_fullchain_path = le_util.unique_file( - fullchain_path, 0o644) - try: - fullchain_file.write(cert_pem + chain_pem) - finally: - fullchain_file.close() - logger.info("Cert chain written to %s", act_fullchain_path) - fullchain_abspath = os.path.abspath(act_fullchain_path) + cert_chain_abspath = _save_chain(chain_pem, chain_path) + fullchain_abspath = _save_chain(cert_pem + chain_pem, + fullchain_path) return os.path.abspath(act_cert_path), cert_chain_abspath, fullchain_abspath @@ -479,3 +457,25 @@ def view_config_changes(config): rev = reverter.Reverter(config) rev.recovery_routine() rev.view_config_changes() + + +def _save_chain(chain_pem, chain_path): + """Saves chain_pem at a unique path based on chain_path. + + :param str chain_pem: certificate chain in PEM format + :param str chain_path: candidate path for the cert chain + + :returns: absolute path to saved cert chain + :rtype: str + + """ + chain_file, act_chain_path = le_util.unique_file(chain_path, 0o644) + try: + chain_file.write(chain_pem) + finally: + chain_file.close() + + logger.info("Cert chain written to %s", act_chain_path) + + # This expects a valid chain file + return os.path.abspath(act_chain_path) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 13613374f..2efe11108 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -114,20 +114,20 @@ class ClientTest(unittest.TestCase): mock.sentinel.key, domains, self.config.csr_dir) self._check_obtain_certificate() - def test_save_certificate(self): # pylint: disable=too-many-locals + def test_save_certificate(self): certs = ["matching_cert.pem", "cert.pem", "cert-san.pem"] tmp_path = tempfile.mkdtemp() os.chmod(tmp_path, 0o755) # TODO: really?? certr = mock.MagicMock(body=test_util.load_cert(certs[0])) - cert1 = test_util.load_cert(certs[1]) - cert2 = test_util.load_cert(certs[2]) + chain_cert = [test_util.load_cert(certs[1]), + test_util.load_cert(certs[2])] candidate_cert_path = os.path.join(tmp_path, "certs", "cert.pem") candidate_chain_path = os.path.join(tmp_path, "chains", "chain.pem") candidate_fullchain_path = os.path.join(tmp_path, "chains", "fullchain.pem") cert_path, chain_path, fullchain_path = self.client.save_certificate( - certr, [cert1, cert2], candidate_cert_path, candidate_chain_path, + certr, chain_cert, candidate_cert_path, candidate_chain_path, candidate_fullchain_path) self.assertEqual(os.path.dirname(cert_path), From ea356b403c0bab88937b31caf4358ab7634399af Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 22 Oct 2015 13:06:33 -0700 Subject: [PATCH 051/216] Removed assumptions about temp dir name --- letsencrypt/tests/renewer_test.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index 28ae74b34..a856fcf4f 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -308,7 +308,8 @@ class RenewableCertTests(BaseRenewableCertTest): def test_update_all_links_to_partial_failure(self): def unlink_or_raise(path, real_unlink=os.unlink): # pylint: disable=missing-docstring - if "fullchain" in path and "prev" in path: + basename = os.path.basename(path) + if "fullchain" in basename and basename.startswith("prev"): raise ValueError else: real_unlink(path) @@ -324,7 +325,7 @@ class RenewableCertTests(BaseRenewableCertTest): def test_update_all_links_to_full_failure(self): def unlink_or_raise(path, real_unlink=os.unlink): # pylint: disable=missing-docstring - if "fullchain" in path: + if "fullchain" in os.path.basename(path): raise ValueError else: real_unlink(path) From 547e0d278da46b5ae966db87e53302b8c3a05d76 Mon Sep 17 00:00:00 2001 From: Joerg Sonnenberger Date: Fri, 23 Oct 2015 01:48:25 +0200 Subject: [PATCH 052/216] Typo --- docs/contributing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 60e7f35c2..26694ff0b 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -293,8 +293,8 @@ directory. .. _prerequisites: -Notes on OS depedencies -======================= +Notes on OS dependencies +======================== OS level dependencies are managed by scripts in ``bootstrap``. Some notes are provided here mainly for the :ref:`developers ` reference. From 5ac682e478071074e35a9139b3403ad097717fb1 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 22 Oct 2015 17:13:57 -0700 Subject: [PATCH 053/216] Added example/cli.ini --- examples/cli.ini | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/cli.ini diff --git a/examples/cli.ini b/examples/cli.ini new file mode 100644 index 000000000..3ea628937 --- /dev/null +++ b/examples/cli.ini @@ -0,0 +1,16 @@ +# This is an example of what you can do in a configuration file + +# make sure to use a valid email and domains! +email = foo@example.com +domains = example.com + +# Uncomment to use a text interface instead of ncurses +# text = True + +# Uncomment to use a 4096 bit RSA key instead of 2048 +# rsa-key-size = 4096 + +# Uncomment to use the webroot authenticator. Replace webroot-path with the +# path to the public_html / webroot folder being served by your web server. +# authenticator = webroot +# webroot-path = /usr/share/nginx/html From 5deba95423b05ad013e3464d9efa7d13528c0852 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Fri, 23 Oct 2015 19:01:06 +0000 Subject: [PATCH 054/216] RTD: install local deps for subpkgs (fixes #1086). --- letsencrypt-apache/readthedocs.org.requirements.txt | 2 ++ letsencrypt-compatibility-test/readthedocs.org.requirements.txt | 2 ++ letsencrypt-nginx/readthedocs.org.requirements.txt | 2 ++ readthedocs.org.requirements.txt | 1 + 4 files changed, 7 insertions(+) diff --git a/letsencrypt-apache/readthedocs.org.requirements.txt b/letsencrypt-apache/readthedocs.org.requirements.txt index 9e782a01e..7855b5ce2 100644 --- a/letsencrypt-apache/readthedocs.org.requirements.txt +++ b/letsencrypt-apache/readthedocs.org.requirements.txt @@ -7,4 +7,6 @@ # in --editable mode (-e), just "pip install .[docs]" does not work as # expected and "pip install -e .[docs]" must be used instead +-e acme +-e . -e letsencrypt-apache[docs] diff --git a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt index 86d680426..208677a35 100644 --- a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt +++ b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt @@ -7,4 +7,6 @@ # in --editable mode (-e), just "pip install .[docs]" does not work as # expected and "pip install -e .[docs]" must be used instead +-e acme +-e . -e letsencrypt-compatibility-test[docs] diff --git a/letsencrypt-nginx/readthedocs.org.requirements.txt b/letsencrypt-nginx/readthedocs.org.requirements.txt index 9a36ed259..3b55df408 100644 --- a/letsencrypt-nginx/readthedocs.org.requirements.txt +++ b/letsencrypt-nginx/readthedocs.org.requirements.txt @@ -7,4 +7,6 @@ # in --editable mode (-e), just "pip install .[docs]" does not work as # expected and "pip install -e .[docs]" must be used instead +-e acme +-e . -e letsencrypt-nginx[docs] diff --git a/readthedocs.org.requirements.txt b/readthedocs.org.requirements.txt index 27cccb0a6..94a81e788 100644 --- a/readthedocs.org.requirements.txt +++ b/readthedocs.org.requirements.txt @@ -7,4 +7,5 @@ # in --editable mode (-e), just "pip install .[docs]" does not work as # expected and "pip install -e .[docs]" must be used instead +-e acme -e .[docs] From 08618466166b0909a1c9d06a6fd4eeae29aab92b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Fri, 23 Oct 2015 19:15:54 +0000 Subject: [PATCH 055/216] RTD: proper deps for compatibility-test --- letsencrypt-compatibility-test/readthedocs.org.requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt index 208677a35..49fab4066 100644 --- a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt +++ b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt @@ -9,4 +9,6 @@ -e acme -e . +-e letsencrypt-apache +-e letsencrypt-nginx -e letsencrypt-compatibility-test[docs] From 41891512c2cf4f50ef342905cc1e8fddc69e1c7c Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Fri, 23 Oct 2015 19:22:05 +0000 Subject: [PATCH 056/216] compatibility-test doesn't need nginx --- letsencrypt-compatibility-test/readthedocs.org.requirements.txt | 1 - letsencrypt-compatibility-test/setup.py | 1 - 2 files changed, 2 deletions(-) diff --git a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt index 49fab4066..957a8a157 100644 --- a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt +++ b/letsencrypt-compatibility-test/readthedocs.org.requirements.txt @@ -10,5 +10,4 @@ -e acme -e . -e letsencrypt-apache --e letsencrypt-nginx -e letsencrypt-compatibility-test[docs] diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 3bd8f274e..1608769e2 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -9,7 +9,6 @@ version = '0.1.0.dev0' install_requires = [ 'letsencrypt=={0}'.format(version), 'letsencrypt-apache=={0}'.format(version), - 'letsencrypt-nginx=={0}'.format(version), 'docker-py', 'zope.interface', ] From e4d4b0d5288519724e107053754d5627d58fb8db Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 14:48:40 -0700 Subject: [PATCH 057/216] Quick doc fix --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 822f231ef..3bf5686fb 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -341,7 +341,7 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): :param str cfg_type: either "installer" or "authenticator" :param str requested: the plugin that was requested - :param PluginRegistry plugins: available plugins + :param .PluginsRegistry plugins: available plugins :raises error.PluginSelectionError: if there was a problem """ From 027a7d755d593e7c00be00df83c6909feffa53d5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 14:58:55 -0700 Subject: [PATCH 058/216] Update developer docs - Discuss letsencrypt-dev - Be clearer in general --- docs/contributing.rst | 188 +++++++++++++++++++++++++----------------- 1 file changed, 112 insertions(+), 76 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 60e7f35c2..d7feb9edc 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -7,40 +7,33 @@ Contributing Hacking ======= -All changes in your pull request **must** have 100% unit test coverage, pass -our `integration`_ tests, **and** be compliant with the -:ref:`coding style `. +Running a local copy of the client +---------------------------------- - -Bootstrap ---------- - -Start by :ref:`installing Let's Encrypt prerequisites -`. Then run: +Running the client in developer mode from your local tree is very similar to +running the released versions of the client; just replace ``letsencrypt-auto`` +with ``letsencrypt-dev``: .. code-block:: shell - ./bootstrap/dev/venv.sh + git clone https://github.com/letsencrypt/letsencrypt + cd letsencrypt + ./letsencrypt-dev -Activate the virtualenv: -.. code-block:: shell +Find issues to work on +---------------------- - source ./$VENV_NAME/bin/activate +You can find the open issues in the `github issue tracker`_. If you're +starting work on something, post a comment to let others know and seek +feedback on your plan where appropriate. -This step should prepend you prompt with ``($VENV_NAME)`` and save you -from typing ``./$VENV_NAME/bin/...``. It is also required to run some -of the `testing`_ tools. Virtualenv can be disabled at any time by -typing ``deactivate``. More information can be found in `virtualenv -documentation`_. - -Note that packages are installed in so called *editable mode*, in -which any source code changes in the current working directory are -"live" and no further ``./bootstrap/dev/venv.sh`` or ``pip install -...`` invocations are necessary while developing. - -.. _`virtualenv documentation`: https://virtualenv.pypa.io +Once you've got a working branch, you can open a pull request. All changes in +your pull request must have thorough unit test coverage, pass our +`integration`_ tests, and be compliant with the :ref:`coding style +`. +.. _github issue tracker: https://github.com/letsencrypt/letsencrypt/issues Testing ------- @@ -64,8 +57,12 @@ The following tools are there to help you: but you won't get TAB completion... -Integration -~~~~~~~~~~~ +Integration testing with the boulder CA +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Generally it is sufficient to open a pull request and let Github and Travis run +integration tests for you. + Mac OS X users: Run `./tests/mac-bootstrap.sh` instead of `boulder-start.sh` to install dependencies, configure the environment, and start boulder. @@ -91,55 +88,6 @@ the integration tests suite. .. _Go: https://golang.org -Vagrant -------- - -If you are a Vagrant user, Let's Encrypt comes with a Vagrantfile that -automates setting up a development environment in an Ubuntu 14.04 -LTS VM. To set it up, simply run ``vagrant up``. The repository is -synced to ``/vagrant``, so you can get started with: - -.. code-block:: shell - - vagrant ssh - cd /vagrant - ./venv/bin/pip install -r requirements.txt .[dev,docs,testing] - sudo ./venv/bin/letsencrypt - -Support for other Linux distributions coming soon. - -.. note:: - Unfortunately, Python distutils and, by extension, setup.py and - tox, use hard linking quite extensively. Hard linking is not - supported by the default sync filesystem in Vagrant. As a result, - all actions with these commands are *significantly slower* in - Vagrant. One potential fix is to `use NFS`_ (`related issue`_). - -.. _use NFS: http://docs.vagrantup.com/v2/synced-folders/nfs.html -.. _related issue: https://github.com/ClusterHQ/flocker/issues/516 - - -Docker ------- - -OSX users will probably find it easiest to set up a Docker container for -development. Let's Encrypt comes with a Dockerfile (``Dockerfile-dev``) -for doing so. To use Docker on OSX, install and setup docker-machine using the -instructions at https://docs.docker.com/installation/mac/. - -To build the development Docker image:: - - docker build -t letsencrypt -f Dockerfile-dev . - -Now run tests inside the Docker image: - -.. code-block:: shell - - docker run -it letsencrypt bash - cd src - tox -e py27 - - Code components and layout ========================== @@ -293,6 +241,94 @@ directory. .. _prerequisites: + +Other methods for running the client +==================================== + +Lower level venv scripts +------------------------ + +You can get slightly lower level exposure to virtualenv by using these +scripts as an alternative to ``letsencrypt-dev``. + +these +by :ref:`installing Let's Encrypt prerequisites `. Then run: + +.. code-block:: shell + + ./bootstrap/dev/venv.sh + +Activate the virtualenv: + +.. code-block:: shell + + source ./$VENV_NAME/bin/activate + +This step should prepend you prompt with ``($VENV_NAME)`` and save you +from typing ``./$VENV_NAME/bin/...``. It is also required to run some +of the `testing`_ tools. Virtualenv can be disabled at any time by +typing ``deactivate``. More information can be found in `virtualenv +documentation`_. + +Note that packages are installed in so called *editable mode*, in +which any source code changes in the current working directory are +"live" and no further ``./bootstrap/dev/venv.sh`` or ``pip install +...`` invocations are necessary while developing. + +.. _`virtualenv documentation`: https://virtualenv.pypa.io + + + +Vagrant +------- + +If you are a Vagrant user, Let's Encrypt comes with a Vagrantfile that +automates setting up a development environment in an Ubuntu 14.04 +LTS VM. To set it up, simply run ``vagrant up``. The repository is +synced to ``/vagrant``, so you can get started with: + +.. code-block:: shell + + vagrant ssh + cd /vagrant + ./venv/bin/pip install -r requirements.txt .[dev,docs,testing] + sudo ./venv/bin/letsencrypt + +Support for other Linux distributions coming soon. + +.. note:: + Unfortunately, Python distutils and, by extension, setup.py and + tox, use hard linking quite extensively. Hard linking is not + supported by the default sync filesystem in Vagrant. As a result, + all actions with these commands are *significantly slower* in + Vagrant. One potential fix is to `use NFS`_ (`related issue`_). + +.. _use NFS: http://docs.vagrantup.com/v2/synced-folders/nfs.html +.. _related issue: https://github.com/ClusterHQ/flocker/issues/516 + + +Docker +------ + +OSX users will probably find it easiest to set up a Docker container for +development. Let's Encrypt comes with a Dockerfile (``Dockerfile-dev``) +for doing so. To use Docker on OSX, install and setup docker-machine using the +instructions at https://docs.docker.com/installation/mac/. + +To build the development Docker image:: + + docker build -t letsencrypt -f Dockerfile-dev . + +Now run tests inside the Docker image: + +.. code-block:: shell + + docker run -it letsencrypt bash + cd src + tox -e py27 + + + Notes on OS depedencies ======================= From ad56bbbceb486cf1b189a17f46ce3b6f98d557e8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 22 Oct 2015 17:30:37 -0700 Subject: [PATCH 059/216] Cleanup plugin docs --- docs/contributing.rst | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index d7feb9edc..61621424a 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -119,7 +119,8 @@ which implement bindings to alternative UI libraries. Authenticators -------------- -Authenticators are plugins designed to solve challenges received from +Authenticators are plugins designed prove that this client deserves a +certificate for some domain name by solving challenges received from the ACME server. From the protocol, there are essentially two different types of challenges. Challenges that must be solved by individual plugins in order to satisfy domain validation (subclasses @@ -143,17 +144,23 @@ in a separate branch). Installer --------- -Installers classes exist to actually setup the certificate and be able -to enhance the configuration. (Turn on HSTS, redirect to HTTPS, -etc). You can indicate your abilities through the -:meth:`~.IInstaller.supported_enhancements` call. We currently only +Installers classes exist to actually setup the certificate in a server, +possibly tweak the security configuration to make it more correct and secure +(Fix some mixed content problems, turn on HSTS, redirect to HTTPS, etc). +Installer plugins tell the main client about their abilities to do the latter +via the :meth:`~.IInstaller.supported_enhancements` call. We currently only have one Installer written (still developing), `~.ApacheConfigurator`. -Installers and Authenticators will oftentimes be the same -class/object. Installers and Authenticators are kept separate because +Installers and Authenticators will oftentimes be the same class/object +(because for instance both tasks can be performed by a webserver like nginx) +though this is not always the case (the standalone plugin is an authenticator +that listens on port 443, but it cannot install certs; a postfix plugin would +be an installer but not an authenticator). + +Installers and Authenticators are kept separate because it should be possible to use the `~.StandaloneAuthenticator` (it sets up its own Python server to perform challenges) with a program that -cannot solve challenges itself. (Imagine MTA installers). +cannot solve challenges itself (Such as MTA installers). Installer Development From 096b5ec32ade4ba33cf976f289c3b853d94a22c2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 16:12:33 -0700 Subject: [PATCH 060/216] Back out ./letsencrypt-dev; document "activate" more clearly --- bootstrap/install-deps.sh | 40 ++++++++++++++++++++++++++++++++++++ docs/contributing.rst | 43 ++++++++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 bootstrap/install-deps.sh diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh new file mode 100644 index 000000000..159027c84 --- /dev/null +++ b/bootstrap/install-deps.sh @@ -0,0 +1,40 @@ +#!/bin/sh -e +# +# Install OS dependencies. In the glorious future, letsencrypt-auto will +# source this... + +if test "`id -u`" -ne "0" ; then + SUDO=sudo +else + SUDO= +fi + +BOOTSTRAP=`dirname $0`/bootstrap +if [ ! -f $BOOTSTRAP/debian.sh ] ; then + echo "Cannot find the letsencrypt bootstrap scripts in $BOOTSTRAP" + exit 1 +fi +if [ -f /etc/debian_version ] ; then + echo "Bootstrapping dependencies for Debian-based OSes..." + $SUDO $BOOTSTRAP/_deb_common.sh +elif [ -f /etc/arch-release ] ; then + echo "Bootstrapping dependencies for Archlinux..." + $SUDO $BOOTSTRAP/archlinux.sh +elif [ -f /etc/redhat-release ] ; then + echo "Bootstrapping dependencies for RedHat-based OSes..." + $SUDO $BOOTSTRAP/_rpm_common.sh +elif uname | grep -iq FreeBSD ; then + echo "Bootstrapping dependencies for FreeBSD..." + $SUDO $BOOTSTRAP/freebsd.sh +elif uname | grep -iq Darwin ; then + echo "Bootstrapping dependencies for Mac OS X..." + echo "WARNING: Mac support is very experimental at present..." + $BOOTSTRAP/mac.sh +else + echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo + echo "You will need to bootstrap, configure virtualenv, and run a pip install manually" + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "for more info" + exit 1 +fi diff --git a/docs/contributing.rst b/docs/contributing.rst index 61621424a..a6c93de4a 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -10,23 +10,41 @@ Hacking Running a local copy of the client ---------------------------------- -Running the client in developer mode from your local tree is very similar to -running the released versions of the client; just replace ``letsencrypt-auto`` -with ``letsencrypt-dev``: +Running the client in developer mode from your local tree is a little +different than running ``letsencrypt-auto``. To get set up, do these things +once: .. code-block:: shell git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt - ./letsencrypt-dev + ./bootstrap/install-deps.sh + ./bootstrap/dev/venv.sh + +Then in each shell where you're working on the client, do: + +.. code-block:: shell + + ./venv/bin/activate + +After that, your shell will be using the virtual environment, and you run the +client by typing: + +.. code-block:: shell + + letsencrypt + +Activating a shell in this way makes it easier to run unit tests +with ``tox`` and integration tests, as described below. Find issues to work on ---------------------- -You can find the open issues in the `github issue tracker`_. If you're -starting work on something, post a comment to let others know and seek -feedback on your plan where appropriate. +You can find the open issues in the `github issue tracker`_. Comparatively +easy ones are marked `Good Volunteer Task`_. If you're starting work on +something, post a comment to let others know and seek feedback on your plan +where appropriate. Once you've got a working branch, you can open a pull request. All changes in your pull request must have thorough unit test coverage, pass our @@ -34,6 +52,7 @@ your pull request must have thorough unit test coverage, pass our `. .. _github issue tracker: https://github.com/letsencrypt/letsencrypt/issues +.. _Good Volunteer Task: https://github.com/letsencrypt/letsencrypt/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 Testing ------- @@ -60,6 +79,8 @@ The following tools are there to help you: Integration testing with the boulder CA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _integration: + Generally it is sufficient to open a pull request and let Github and Travis run integration tests for you. @@ -144,12 +165,14 @@ in a separate branch). Installer --------- -Installers classes exist to actually setup the certificate in a server, +Installers plugins exist to actually setup the certificate in a server, possibly tweak the security configuration to make it more correct and secure (Fix some mixed content problems, turn on HSTS, redirect to HTTPS, etc). Installer plugins tell the main client about their abilities to do the latter -via the :meth:`~.IInstaller.supported_enhancements` call. We currently only -have one Installer written (still developing), `~.ApacheConfigurator`. +via the :meth:`~.IInstaller.supported_enhancements` call. We currently +have two Installers in the tree, the `~.ApacheConfigurator`. and the +`~.NginxConfigurator`. External projects have made some progress toward +support for IIS, Icecast and Plesk. Installers and Authenticators will oftentimes be the same class/object (because for instance both tasks can be performed by a webserver like nginx) From e19e0dc2e9b6289ad3d7a8649344d6b9f9e83d6b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 16:18:08 -0700 Subject: [PATCH 061/216] This section is now redundant --- docs/contributing.rst | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index a6c93de4a..a32bc0e63 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -275,40 +275,6 @@ directory. Other methods for running the client ==================================== -Lower level venv scripts ------------------------- - -You can get slightly lower level exposure to virtualenv by using these -scripts as an alternative to ``letsencrypt-dev``. - -these -by :ref:`installing Let's Encrypt prerequisites `. Then run: - -.. code-block:: shell - - ./bootstrap/dev/venv.sh - -Activate the virtualenv: - -.. code-block:: shell - - source ./$VENV_NAME/bin/activate - -This step should prepend you prompt with ``($VENV_NAME)`` and save you -from typing ``./$VENV_NAME/bin/...``. It is also required to run some -of the `testing`_ tools. Virtualenv can be disabled at any time by -typing ``deactivate``. More information can be found in `virtualenv -documentation`_. - -Note that packages are installed in so called *editable mode*, in -which any source code changes in the current working directory are -"live" and no further ``./bootstrap/dev/venv.sh`` or ``pip install -...`` invocations are necessary while developing. - -.. _`virtualenv documentation`: https://virtualenv.pypa.io - - - Vagrant ------- From 7b0f3b00f257d19f1f14209e1fbd22e6a1f650e3 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 16:49:38 -0700 Subject: [PATCH 062/216] Removed subparsers --- letsencrypt/cli.py | 101 +++++++++++++++++----------------- letsencrypt/tests/cli_test.py | 28 +++++++++- 2 files changed, 77 insertions(+), 52 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 3bf5686fb..8edea5470 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -53,11 +53,7 @@ SHORT_USAGE = """ The Let's Encrypt agent can obtain and install HTTPS/TLS/SSL certificates. By default, it will attempt to use a webserver both for obtaining and installing -the cert. """ - -# This is the short help for letsencrypt --help, where we disable argparse -# altogether -USAGE = SHORT_USAGE + """Major SUBCOMMANDS are: +the cert. Major SUBCOMMANDS are: (default) run Obtain & install a cert in your current webserver auth Authenticate & obtain cert, but do not install it @@ -66,7 +62,12 @@ USAGE = SHORT_USAGE + """Major SUBCOMMANDS are: rollback Rollback server configuration changes made during install config_changes Show changes made to server config during installation -Choice of server for authentication/installation: +""" + + +# This is the short help for letsencrypt --help, where we disable argparse +# altogether +USAGE = SHORT_USAGE + """Choice of server for authentication/installation: --apache Use the Apache plugin for authentication & installation --nginx Use the Nginx plugin for authentication & installation @@ -604,9 +605,19 @@ class HelpfulArgumentParser(object): 'letsencrypt --help security' for security options. """ + + # Maps verbs/subcommands to the functions that implement them + VERBS = {"auth": auth, "config_changes": config_changes, + "install": install, "plugins": plugins_cmd, + "revoke": revoke, "rollback": rollback, "run": run} + + # List of topics for which additional help can be provided + HELP_TOPICS = ["all", "security", + "paths", "automation", "testing"] + VERBS.keys() + def __init__(self, args, plugins): plugin_names = [name for name, _p in plugins.iteritems()] - self.help_topics = HELP_TOPICS + plugin_names + [None] + self.help_topics = self.HELP_TOPICS + plugin_names + [None] self.parser = configargparse.ArgParser( usage=SHORT_USAGE, formatter_class=argparse.ArgumentDefaultsHelpFormatter, @@ -617,8 +628,8 @@ class HelpfulArgumentParser(object): self.parser._add_config_file_help = False # pylint: disable=protected-access self.silent_parser = SilentParser(self.parser) - self.verb = None - self.args = self.preprocess_args(args) + self.args = args + self.determine_verb() help1 = self.prescan_for_flag("-h", self.help_topics) help2 = self.prescan_for_flag("--help", self.help_topics) assert max(True, "a") == "a", "Gravity changed direction" @@ -631,25 +642,32 @@ class HelpfulArgumentParser(object): #print self.visible_topics self.groups = {} # elements are added by .add_group() - def preprocess_args(self, args): - """Work around some limitations in argparse. + def parse_args(self): + """Parses command line arguments and returns the result. + + :returns: parsed command line arguments + :rtype: argparse.Namespace - Currently: add the default verb "run" as a default, and ensure that the - subcommand / verb comes last. """ - if "-h" in args or "--help" in args: + parsed_args = self.parser.parse_args(self.args) + parsed_args.func = self.VERBS[self.verb] + + return parsed_args + + def determine_verb(self): + """Determines the verb/subcommand provided by the user.""" + if "-h" in self.args or "--help" in self.args: # all verbs double as help arguments; don't get them confused self.verb = "help" - return args + return - for i, token in enumerate(args): - if token in VERBS: - reordered = args[:i] + args[(i + 1):] + [args[i]] + for i, token in enumerate(self.args): + if token in self.VERBS: self.verb = token - return reordered + self.args.pop(i) + return self.verb = "run" - return args + ["run"] def prescan_for_flag(self, flag, possible_arguments): """Checks cli input for flags. @@ -738,8 +756,16 @@ class HelpfulArgumentParser(object): return dict([(t, t == chosen_topic) for t in self.help_topics]) -def create_parser(plugins, args): - """Create parser.""" +def parse_args(plugins, args): + """Returns parsed command line arguments. + + :param .PluginsRegistry plugins: available plugins + :param list args: command line arguments with the program name removed + + :returns: parsed command line arguments + :rtype: argparse.Namespace + + """ helpful = HelpfulArgumentParser(args, plugins) # --help is automatically provided by argparse @@ -821,36 +847,10 @@ def create_parser(plugins, args): _create_subparsers(helpful) - return helpful.parser, helpful.args - - -# For now unfortunately this constant just needs to match the code below; -# there isn't an elegant way to autogenerate it in time. -VERBS = ["run", "auth", "install", "revoke", "rollback", "config_changes", "plugins"] -HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + VERBS + return helpful.parse_args() def _create_subparsers(helpful): - subparsers = helpful.parser.add_subparsers(metavar="SUBCOMMAND") - - def add_subparser(name): # pylint: disable=missing-docstring - if name == "plugins": - func = plugins_cmd - else: - func = eval(name) # pylint: disable=eval-used - h = func.__doc__.splitlines()[0] - subparser = subparsers.add_parser(name, help=h, description=func.__doc__) - subparser.set_defaults(func=func) - return subparser - - # the order of add_subparser() calls is important: it defines the - # order in which subparser names will be displayed in --help - # these add_subparser objects return objects to which arguments could be - # attached, but they have annoying arg ordering constrains so we use - # groups instead: https://github.com/letsencrypt/letsencrypt/issues/820 - for v in VERBS: - add_subparser(v) - helpful.add_group("auth", description="Options for modifying how a cert is obtained") helpful.add_group("install", description="Options for modifying how a cert is deployed") helpful.add_group("revoke", description="Options for revocation of certs") @@ -1040,8 +1040,7 @@ def main(cli_args=sys.argv[1:]): # note: arg parser internally handles --help (and exits afterwards) plugins = plugins_disco.PluginsRegistry.find_all() - parser, tweaked_cli_args = create_parser(plugins, cli_args) - args = parser.parse_args(tweaked_cli_args) + args = parse_args(plugins, cli_args) config = configuration.NamespaceConfig(args) zope.component.provideUtility(config) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 03f291723..86c6a11bc 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -60,7 +60,7 @@ class CLITest(unittest.TestCase): return ret, None, stderr, client def test_no_flags(self): - with mock.patch('letsencrypt.cli.run') as mock_run: + with MockedVerb("run") as mock_run: self._call([]) self.assertEqual(1, mock_run.call_count) @@ -368,5 +368,31 @@ class DuplicativeCertsTest(renewer_test.BaseRenewableCertTest): self.assertEqual(result, (None, None)) +class MockedVerb(object): + """Simple class that can be used for mocking out verbs/subcommands. + + Storing a dictionary of verbs and the functions that implement them + makes mocking much more complicated. This class can be used as a + simple context manager for mocking out verbs in CLI tests. + + """ + def __init__(self, verb_name): + from letsencrypt import cli + + self.verb_dict = cli.HelpfulArgumentParser.VERBS + self.verb_func = None + self.verb_name = verb_name + + def __enter__(self): + self.verb_func = self.verb_dict[self.verb_name] + mocked_func = mock.MagicMock() + self.verb_dict[self.verb_name] = mocked_func + + return mocked_func + + def __exit__(self, unused_type, unused_value, unused_trace): + self.verb_dict[self.verb_name] = self.verb_func + + if __name__ == '__main__': unittest.main() # pragma: no cover From 46fe8511cd4b41f97537a9dd9892e630d25c7b91 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 16:55:10 -0700 Subject: [PATCH 063/216] Updated MockedVerb documentation --- letsencrypt/tests/cli_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 86c6a11bc..cb2360dd0 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -372,8 +372,13 @@ class MockedVerb(object): """Simple class that can be used for mocking out verbs/subcommands. Storing a dictionary of verbs and the functions that implement them - makes mocking much more complicated. This class can be used as a - simple context manager for mocking out verbs in CLI tests. + in letsencrypt.cli makes mocking much more complicated. This class + can be used as a simple context manager for mocking out verbs in CLI + tests. For example: + + with MockedVerb("run") as mock_run: + self._call([]) + self.assertEqual(1, mock_run.call_count) """ def __init__(self, verb_name): From a567c0578f8f241e94f3d7652ba020bb7e216ec1 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 17:17:23 -0700 Subject: [PATCH 064/216] Pass plugin errors back out to the user. Closes: #971 --- letsencrypt/cli.py | 3 ++- letsencrypt/plugins/disco.py | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 822f231ef..2aedeea8f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -351,7 +351,8 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): msg = "The requested {0} plugin does not appear to be installed".format(requested) else: msg = ("The {0} plugin is not working; there may be problems with " - "your existing configuration").format(requested) + "your existing configuration.\nThe error was: {1}" + .format(requested, plugins[requested].problem)) elif cfg_type == "installer": if os.path.exists("/etc/debian_version"): # Debian... installers are at least possible diff --git a/letsencrypt/plugins/disco.py b/letsencrypt/plugins/disco.py index 5a41fda88..3f26206c7 100644 --- a/letsencrypt/plugins/disco.py +++ b/letsencrypt/plugins/disco.py @@ -120,6 +120,12 @@ class PluginEntryPoint(object): """Is plugin misconfigured?""" return isinstance(self._prepared, errors.MisconfigurationError) + @property + def problem(self): + """Return the Exception raised during plugin setup, or None if all is well""" + if isinstance(self._prepared, Exception): + return self._prepared + @property def available(self): """Is plugin available, i.e. prepared or misconfigured?""" From f66334a7827bcf08854bcc3f70ac19250600999c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 17:22:26 -0700 Subject: [PATCH 065/216] chmod a+x --- bootstrap/install-deps.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bootstrap/install-deps.sh diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh old mode 100644 new mode 100755 From aa3f4933419232767952175e00343ee1f71d9774 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 17:23:26 -0700 Subject: [PATCH 066/216] Fix fragment link nit --- docs/contributing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 9dfc35882..9faf82582 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -76,11 +76,11 @@ The following tools are there to help you: but you won't get TAB completion... +.. _integration: + Integration testing with the boulder CA ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _integration: - Generally it is sufficient to open a pull request and let Github and Travis run integration tests for you. From 6b6a20b5e50db568dc0e59fa205124c524ebd89a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 17:24:17 -0700 Subject: [PATCH 067/216] typo --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 9faf82582..118906c0c 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -140,7 +140,7 @@ which implement bindings to alternative UI libraries. Authenticators -------------- -Authenticators are plugins designed prove that this client deserves a +Authenticators are plugins designed to prove that this client deserves a certificate for some domain name by solving challenges received from the ACME server. From the protocol, there are essentially two different types of challenges. Challenges that must be solved by From efa1d6d38e4c56c03c5d5c6da7d8449e3a28367b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 17:27:55 -0700 Subject: [PATCH 068/216] Reintroduce a bit more verbosity about virtualenv? --- docs/contributing.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 118906c0c..7859e14bc 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -35,8 +35,10 @@ client by typing: letsencrypt Activating a shell in this way makes it easier to run unit tests -with ``tox`` and integration tests, as described below. +with ``tox`` and integration tests, as described below. To reverse this, you +can type ``deactivate``. More information can be found in the `virtualenv docs`_. +.. _`virtualenv docs`: https://virtualenv.pypa.io Find issues to work on ---------------------- From 9ba4748974baf507968ccc2e3ffbabe691614436 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 18:26:33 -0700 Subject: [PATCH 069/216] Incorporated pde's feedback --- letsencrypt/cli.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 8edea5470..274db04ff 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -655,7 +655,11 @@ class HelpfulArgumentParser(object): return parsed_args def determine_verb(self): - """Determines the verb/subcommand provided by the user.""" + """Determines the verb/subcommand provided by the user. + + This function works around some of the limitations of argparse. + + """ if "-h" in self.args or "--help" in self.args: # all verbs double as help arguments; don't get them confused self.verb = "help" @@ -756,7 +760,7 @@ class HelpfulArgumentParser(object): return dict([(t, t == chosen_topic) for t in self.help_topics]) -def parse_args(plugins, args): +def prepare_and_parse_args(plugins, args): """Returns parsed command line arguments. :param .PluginsRegistry plugins: available plugins @@ -1040,7 +1044,7 @@ def main(cli_args=sys.argv[1:]): # note: arg parser internally handles --help (and exits afterwards) plugins = plugins_disco.PluginsRegistry.find_all() - args = parse_args(plugins, cli_args) + args = prepare_and_parse_args(plugins, cli_args) config = configuration.NamespaceConfig(args) zope.component.provideUtility(config) From d4ed2d44e157c8b23b8b093141377701e220ad70 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 18:44:33 -0700 Subject: [PATCH 070/216] source --- docs/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 7859e14bc..395493cef 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -25,7 +25,7 @@ Then in each shell where you're working on the client, do: .. code-block:: shell - ./venv/bin/activate + source ./venv/bin/activate After that, your shell will be using the virtual environment, and you run the client by typing: From c95ef0d4aa12ae27707aa36069d51e397a930091 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 19:01:08 -0700 Subject: [PATCH 071/216] Updated examples/cli.ini --- examples/cli.ini | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/cli.ini b/examples/cli.ini index 3ea628937..15fb89054 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -1,8 +1,7 @@ -# This is an example of what you can do in a configuration file +# This is an example of the kind of things you can do in a configuration file -# make sure to use a valid email and domains! -email = foo@example.com -domains = example.com +# Uncomment and update to register with the specified e-mail address +# email = foo@example.com # Uncomment to use a text interface instead of ncurses # text = True @@ -10,6 +9,13 @@ domains = example.com # Uncomment to use a 4096 bit RSA key instead of 2048 # rsa-key-size = 4096 +# Uncomment to always use the staging/testing server +# server = https://acme-staging.api.letsencrypt.org/directory + +# Uncomment to use the standalone authenticator on port 443 +# authenticator = standalone +# standalone-supported-challenges = dvsni + # Uncomment to use the webroot authenticator. Replace webroot-path with the # path to the public_html / webroot folder being served by your web server. # authenticator = webroot From 4351d3d7ecd7f6cbaf65ba98da1ee260551a200b Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 19:10:56 -0700 Subject: [PATCH 072/216] Updated docs/using.rst --- docs/using.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 879ea49c4..9ac745c61 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -50,11 +50,10 @@ Configuration file ------------------ It is possible to specify configuration file with -``letsencrypt-auto --config cli.ini`` (or shorter ``-c cli.ini``). For -instance, if you are a contributor, you might find the following -handy: +``letsencrypt-auto --config cli.ini`` (or shorter ``-c cli.ini``). An +example configuration file is shown below: -.. include:: ../examples/dev-cli.ini +.. include:: ../examples/cli.ini :code: ini By default, the following locations are searched: From b2c27df19917897531e852933e319695d8666381 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 23 Oct 2015 19:15:00 -0700 Subject: [PATCH 073/216] Uncommented lines because CSS --- examples/cli.ini | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/cli.ini b/examples/cli.ini index 15fb89054..f06cc51ef 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -1,17 +1,17 @@ # This is an example of the kind of things you can do in a configuration file +# Use a 4096 bit RSA key instead of 2048 +rsa-key-size = 4096 + +# Always use the staging/testing server +server = https://acme-staging.api.letsencrypt.org/directory + # Uncomment and update to register with the specified e-mail address # email = foo@example.com # Uncomment to use a text interface instead of ncurses # text = True -# Uncomment to use a 4096 bit RSA key instead of 2048 -# rsa-key-size = 4096 - -# Uncomment to always use the staging/testing server -# server = https://acme-staging.api.letsencrypt.org/directory - # Uncomment to use the standalone authenticator on port 443 # authenticator = standalone # standalone-supported-challenges = dvsni From ce32d87ad6eab0c6a3cb4b652a778db2aaf62b23 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 23 Oct 2015 19:19:09 -0700 Subject: [PATCH 074/216] Document the requirement for SANs in CSRs if using --csr --- letsencrypt/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 822f231ef..8bd15d404 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -859,7 +859,9 @@ def _create_subparsers(helpful): helpful.add("auth", "--csr", type=read_file, - help="Path to a Certificate Signing Request (CSR) in DER format.") + help="Path to a Certificate Signing Request (CSR) in DER" + " format; note that the .csr file *must* contain a Subject" + " Alternative Name field for each domain you want certified") helpful.add("rollback", "--checkpoints", type=int, metavar="N", default=flag_default("rollback_checkpoints"), From ca838b0ea37b5440d25b88ecea3d1d71c7b7c3c4 Mon Sep 17 00:00:00 2001 From: Joubin Jabbari Date: Sat, 24 Oct 2015 00:02:13 -0700 Subject: [PATCH 075/216] removed extra path ``` root@localhost:~# cd letsencrypt/ root@localhost:~/letsencrypt# ./bootstrap/install-deps.sh Cannot find the letsencrypt bootstrap scripts in ./bootstrap/bootstrap ``` However, with the proposed change ``` root@localhost:~/letsencrypt# ./bootstrap/install-deps.sh Bootstrapping dependencies for Debian-based OSes... Ign http://mirrors.linode.com jessie InRelease Hit http://mirrors.linode.com jessie-updates InRelease ``` --- bootstrap/install-deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh index 159027c84..c7247c5d1 100755 --- a/bootstrap/install-deps.sh +++ b/bootstrap/install-deps.sh @@ -9,7 +9,7 @@ else SUDO= fi -BOOTSTRAP=`dirname $0`/bootstrap +BOOTSTRAP=`dirname $0`/ if [ ! -f $BOOTSTRAP/debian.sh ] ; then echo "Cannot find the letsencrypt bootstrap scripts in $BOOTSTRAP" exit 1 From 3386cd8711395794a1e943be39e670cf9c16f2c2 Mon Sep 17 00:00:00 2001 From: Joubin Jabbari Date: Sat, 24 Oct 2015 00:27:12 -0700 Subject: [PATCH 076/216] removed trading slash --- bootstrap/install-deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh index c7247c5d1..c159858c5 100755 --- a/bootstrap/install-deps.sh +++ b/bootstrap/install-deps.sh @@ -9,7 +9,7 @@ else SUDO= fi -BOOTSTRAP=`dirname $0`/ +BOOTSTRAP=`dirname $0` if [ ! -f $BOOTSTRAP/debian.sh ] ; then echo "Cannot find the letsencrypt bootstrap scripts in $BOOTSTRAP" exit 1 From 673d6b10b3b796c23f847b7826df11b7cb815e35 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:20:26 +0000 Subject: [PATCH 077/216] Revert old install warning (fixes #1044) --- docs/using.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 879ea49c4..bcbbc20d8 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -35,9 +35,13 @@ To install and run the client you just need to type: ``letsencrypt``. ``letsencrypt-auto`` is a wrapper which installs virtualized dependencies and provides automated updates during the beta program) -.. warning:: Please do **not** use ``python setup.py install`` or ``sudo pip install`. - Those mode of operation might corrupt your operating system and is - **not supported** by the Let's Encrypt team! +.. warning:: Please do **not** use ``python setup.py install``. Please + do **not** attempt the installation commands as + superuser/root and/or without Virtualenv_, e.g. ``sudo + python setup.py install``, ``sudo pip install``, ``sudo + ./venv/bin/...``. These modes of operation might corrupt + your operating system and are **not supported** by the + Let's Encrypt team! The ``letsencrypt`` commandline tool has a builtin help: From 0a6c61551ccfd51b2d082cdcdf3e4a5102d49f16 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:22:46 +0000 Subject: [PATCH 078/216] Warn against pip install, rewrap. --- docs/using.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index bcbbc20d8..995a4c83a 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -35,13 +35,13 @@ To install and run the client you just need to type: ``letsencrypt``. ``letsencrypt-auto`` is a wrapper which installs virtualized dependencies and provides automated updates during the beta program) -.. warning:: Please do **not** use ``python setup.py install``. Please - do **not** attempt the installation commands as - superuser/root and/or without Virtualenv_, e.g. ``sudo - python setup.py install``, ``sudo pip install``, ``sudo - ./venv/bin/...``. These modes of operation might corrupt - your operating system and are **not supported** by the - Let's Encrypt team! +.. warning:: Please do **not** use ``python setup.py install`` or + ``python pip install .``. Please do **not** attempt the + installation commands as superuser/root and/or without Virtualenv_, + e.g. ``sudo python setup.py install``, ``sudo pip install``, ``sudo + ./venv/bin/...``. These modes of operation might corrupt your + operating system and are **not supported** by the Let's Encrypt + team! The ``letsencrypt`` commandline tool has a builtin help: From b021b28bca086b71cc225bf52c85ae3fff1ae720 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:23:23 +0000 Subject: [PATCH 079/216] Remove link for Virtualenv --- docs/using.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 995a4c83a..1e94ab21e 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -37,11 +37,11 @@ dependencies and provides automated updates during the beta program) .. warning:: Please do **not** use ``python setup.py install`` or ``python pip install .``. Please do **not** attempt the - installation commands as superuser/root and/or without Virtualenv_, - e.g. ``sudo python setup.py install``, ``sudo pip install``, ``sudo - ./venv/bin/...``. These modes of operation might corrupt your - operating system and are **not supported** by the Let's Encrypt - team! + installation commands as superuser/root and/or without virtual + environment, e.g. ``sudo python setup.py install``, ``sudo pip + install``, ``sudo ./venv/bin/...``. These modes of operation might + corrupt your operating system and are **not supported** by the + Let's Encrypt team! The ``letsencrypt`` commandline tool has a builtin help: From 0fb8cb5074c8ab0fef510f97402ce20ea68fd142 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:24:44 +0000 Subject: [PATCH 080/216] Move install warns to section bottom --- docs/using.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 1e94ab21e..fe3a26edf 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -35,6 +35,12 @@ To install and run the client you just need to type: ``letsencrypt``. ``letsencrypt-auto`` is a wrapper which installs virtualized dependencies and provides automated updates during the beta program) +The ``letsencrypt`` commandline tool has a builtin help: + +.. code-block:: shell + + ./letsencrypt-auto --help + .. warning:: Please do **not** use ``python setup.py install`` or ``python pip install .``. Please do **not** attempt the installation commands as superuser/root and/or without virtual @@ -43,12 +49,6 @@ dependencies and provides automated updates during the beta program) corrupt your operating system and are **not supported** by the Let's Encrypt team! -The ``letsencrypt`` commandline tool has a builtin help: - -.. code-block:: shell - - ./letsencrypt-auto --help - Configuration file ------------------ From c0a8dae56506a97f8793b7fc206b122b36fdba06 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:29:01 +0000 Subject: [PATCH 081/216] Remove autodoc for standalone.authenticator --- docs/api/plugins/standalone.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/api/plugins/standalone.rst b/docs/api/plugins/standalone.rst index 99e5ea2f8..f5b9d9c24 100644 --- a/docs/api/plugins/standalone.rst +++ b/docs/api/plugins/standalone.rst @@ -3,9 +3,3 @@ .. automodule:: letsencrypt.plugins.standalone :members: - -:mod:`letsencrypt.plugins.standalone.authenticator` -=================================================== - -.. automodule:: letsencrypt.plugins.standalone.authenticator - :members: From e2a7165fa01eadaa69b96367d59282f1eb7cec94 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:38:35 +0000 Subject: [PATCH 082/216] MANIFEST: include examples dir for letsencrypt This is necessary because docs includes files from the examples dir. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index dfec1c3c9..fa0d16e0d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,4 +6,5 @@ include LICENSE.txt include linter_plugin.py include letsencrypt/DISCLAIMER recursive-include docs * +recursive-include examples * recursive-include letsencrypt/tests/testdata * From cd07d3aa27ad140025a6d9a9948e965ebd3d1d55 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 13:39:38 +0000 Subject: [PATCH 083/216] MANIFEST: include examples dir for acme --- acme/MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/acme/MANIFEST.in b/acme/MANIFEST.in index a452d60d4..5367e484a 100644 --- a/acme/MANIFEST.in +++ b/acme/MANIFEST.in @@ -1,4 +1,5 @@ include LICENSE.txt include README.rst recursive-include docs * +recursive-include examples * recursive-include acme/testdata * From 02159624bfbd39bb5029e5ba9c084b9218393e4d Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Sat, 24 Oct 2015 09:20:52 -0700 Subject: [PATCH 084/216] Informed users about other options --- examples/cli.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/cli.ini b/examples/cli.ini index f06cc51ef..34fb8ab02 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -1,4 +1,6 @@ -# This is an example of the kind of things you can do in a configuration file +# This is an example of the kind of things you can do in a configuration file. +# All flags used by the client can be configured here. Run Let's Encrypt with +# "--help" to learn more about the available options. # Use a 4096 bit RSA key instead of 2048 rsa-key-size = 4096 From 0e395ba763a16c030d5e05e4c1551a9c97e3a2bd Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Oct 2015 19:57:24 +0100 Subject: [PATCH 085/216] Close #1030 --- letsencrypt/cli.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 822f231ef..b2c072a37 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -436,6 +436,7 @@ def run(args, config, plugins): # pylint: disable=too-many-branches,too-many-lo le_client.deploy_certificate( domains, lineage.privkey, lineage.cert, lineage.chain, lineage.fullchain) + le_client.enhance_config(domains, args.redirect) if len(lineage.available_versions("cert")) == 1: @@ -808,7 +809,11 @@ def create_parser(plugins, args): helpful.add( "security", "-r", "--redirect", action="store_true", help="Automatically redirect all HTTP traffic to HTTPS for the newly " - "authenticated vhost.") + "authenticated vhost.", dest="redirect", default=None) + helpful.add( + "security", "-n", "--no-redirect", action="store_false", + help="Do not automatically redirect all HTTP traffic to HTTPS for the newly " + "authenticated vhost.", dest="redirect", default=None) helpful.add( "security", "--strict-permissions", action="store_true", help="Require that all configuration files are owned by the current " From 1cb787cefee37edc4ba75fde53771255684d756e Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Oct 2015 20:15:25 +0100 Subject: [PATCH 086/216] Remove TODO comment for current issue --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b2c072a37..2651edfa8 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -805,7 +805,6 @@ def create_parser(plugins, args): helpful.add( "security", "-B", "--rsa-key-size", type=int, metavar="N", default=flag_default("rsa_key_size"), help=config_help("rsa_key_size")) - # TODO: resolve - assumes binary logic while client.py assumes ternary. helpful.add( "security", "-r", "--redirect", action="store_true", help="Automatically redirect all HTTP traffic to HTTPS for the newly " @@ -1107,3 +1106,4 @@ if __name__ == "__main__": if err_string: logger.warn("Exiting with message %s", err_string) sys.exit(err_string) # pragma: no cover +v From 9d92a4c4ae2d1ca5e8febcc8ce6f0cbeb5c0d5e8 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Oct 2015 20:38:50 +0100 Subject: [PATCH 087/216] Fix random "v" at bottom of file (stupid nano) --- letsencrypt/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 2651edfa8..ef8700796 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1106,4 +1106,3 @@ if __name__ == "__main__": if err_string: logger.warn("Exiting with message %s", err_string) sys.exit(err_string) # pragma: no cover -v From e3d56b51c4fd7815d7f405f2ca29d4574e0b8990 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 24 Oct 2015 13:14:19 -0700 Subject: [PATCH 088/216] Fix nit. --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 8bd15d404..61b367a9c 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -861,7 +861,7 @@ def _create_subparsers(helpful): "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER" " format; note that the .csr file *must* contain a Subject" - " Alternative Name field for each domain you want certified") + " Alternative Name field for each domain you want certified.") helpful.add("rollback", "--checkpoints", type=int, metavar="N", default=flag_default("rollback_checkpoints"), From 4eab094e5e307c8dd862a71de54f0dae3feca2fe Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:23:14 +0000 Subject: [PATCH 089/216] Add dummy Packaging Guide --- docs/index.rst | 1 + docs/packaging.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 docs/packaging.rst diff --git a/docs/index.rst b/docs/index.rst index 72be096f9..a7df1fb0f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -7,6 +7,7 @@ Welcome to the Let's Encrypt client documentation! intro using contributing + packaging plugins .. toctree:: diff --git a/docs/packaging.rst b/docs/packaging.rst new file mode 100644 index 000000000..84d7aa4ca --- /dev/null +++ b/docs/packaging.rst @@ -0,0 +1,6 @@ +========= +Packaging +========= + +Documentation can be found at +https://github.com/letsencrypt/letsencrypt/wiki/Packaging. From 9c98b5c664b2fe408bdb85eb503a04ad64d6760d Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:22:24 +0000 Subject: [PATCH 090/216] Reorg User Guide --- docs/using.rst | 107 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 883efc78c..74c80887b 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -2,44 +2,95 @@ Using the Let's Encrypt client ============================== +Welcome to the User Guide! -Getting the code -================ +.. contents:: Table of Contents + :local: -Please `install Git`_ and run the following commands: + +Installation +============ + +Unless you have a very specific requirements, we kindly ask you to use +the letsencrypt-auto_ method described below. It's the fastest, the +most thourougly tested and the most reliable way of getting our +software and the free SSL certificates! + +.. letsencrypt-auto: + +letsencrypt-auto +---------------- + +``letsencrypt-auto`` is a wrapper which installs some dependencies +from your OS standard package repostories (e.g using `apt-get` or +`yum`), and for other depencies it sets up a virtualized Python +environment with packages downloaded from PyPI [#venv]_. It also +provides automated updates. + +Firstly, please `install Git`_ and run the following commands: .. code-block:: shell git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt -Alternatively you could `download the ZIP archive`_ and extract the -snapshot of our repository, but it's strongly recommended to use the -above method instead. +.. warning:: Alternatively you could `download the ZIP archive`_ and + extract the snapshot of our repository, but it's strongly + recommended to use the above method instead. .. _`install Git`: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git .. _`download the ZIP archive`: https://github.com/letsencrypt/letsencrypt/archive/master.zip - -Installation and Usage -====================== - To install and run the client you just need to type: .. code-block:: shell ./letsencrypt-auto -(Once letsencrypt is packaged by distributions, the command will just be -``letsencrypt``. ``letsencrypt-auto`` is a wrapper which installs virtualized -dependencies and provides automated updates during the beta program) - -The ``letsencrypt`` commandline tool has a builtin help: +Throughout the documentation, whenever you see references to +``letsencrypt`` script/binary, you can subsitute in +``letsencrypt-auto``. For example, to get the help you would type: .. code-block:: shell - ./letsencrypt-auto --help + ./letsencrypt-auto --help + + +Running with Docker +------------------- + +Docker_ is another way to quickly obtain testing certs. From the +server that the domain your requesting a cert for resolves to, +`install Docker`_, issue the following command: + +.. code-block:: shell + + sudo docker run -it --rm -p 443:443 --name letsencrypt \ + -v "/etc/letsencrypt:/etc/letsencrypt" \ + -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ + quay.io/letsencrypt/letsencrypt:latest auth + +and follow the instructions. Your new cert will be available in +``/etc/letsencrypt/certs``. + +.. _Docker: https://docker.com +.. _`install Docker`: https://docs.docker.com/userguide/ + + +Distro packages +--------------- + +Unfortunately, this is an ongoing effort. If you'd like to package +Let's Encrypt client for your distribution of choice please have a +look at :doc:`packaging`. + + +From source +----------- + +Installation from source is only supported for developers and the +whole process is described in :doc:`contributing`. .. warning:: Please do **not** use ``python setup.py install`` or ``python pip install .``. Please do **not** attempt the @@ -51,7 +102,7 @@ The ``letsencrypt`` commandline tool has a builtin help: Configuration file ------------------- +================== It is possible to specify configuration file with ``letsencrypt-auto --config cli.ini`` (or shorter ``-c cli.ini``). An @@ -70,22 +121,8 @@ By default, the following locations are searched: .. keep it up to date with constants.py -Running with Docker -=================== +.. rubric:: Footnotes -Docker_ is another way to quickly obtain testing certs. From the -server that the domain your requesting a cert for resolves to, -`install Docker`_, issue the following command: - -.. code-block:: shell - - sudo docker run -it --rm -p 443:443 --name letsencrypt \ - -v "/etc/letsencrypt:/etc/letsencrypt" \ - -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ - quay.io/letsencrypt/letsencrypt:latest auth - -and follow the instructions. Your new cert will be available in -``/etc/letsencrypt/certs``. - -.. _Docker: https://docker.com -.. _`install Docker`: https://docs.docker.com/userguide/ +.. [#venv] By using this virtualized Python enviroment (`virtualenv + `_) we don't pollute the main + OS space with packages from PyPI! From 6135fbdc18b4b38903f928ce3720ddaa081a566d Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:26:17 +0000 Subject: [PATCH 091/216] docs: fix prerequisites ref --- docs/contributing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 395493cef..9d40aafd3 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -271,8 +271,6 @@ commands: This should generate documentation in the ``docs/_build/html`` directory. -.. _prerequisites: - Other methods for running the client ==================================== @@ -326,6 +324,8 @@ Now run tests inside the Docker image: tox -e py27 +.. _prerequisites: + Notes on OS dependencies ======================== From c84e9b295073ad247a63eebeff29d109dce5ab0b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:28:01 +0000 Subject: [PATCH 092/216] Add TOC to dev guide --- docs/contributing.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index 9d40aafd3..161523631 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -2,6 +2,12 @@ Contributing ============ +Welcome to the Developer's Guide! + +.. contents:: Table of Contents + :local: + + .. _hacking: Hacking From d079df9cd5962013ef1cc13bb836f168a0433dae Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:31:41 +0000 Subject: [PATCH 093/216] docs: move plugins.rst to dev guide section --- docs/contributing.rst | 20 ++++++++++++++++++++ docs/index.rst | 1 - docs/plugins.rst | 19 ------------------- 3 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 docs/plugins.rst diff --git a/docs/contributing.rst b/docs/contributing.rst index 161523631..9166d7d8e 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -214,6 +214,26 @@ plugins implement the `~letsencrypt.interfaces.IDisplay` interface. +Writing your own plugin +======================= + +Let's Encrypt client supports dynamic discovery of plugins through the +`setuptools entry points`_. This way you can, for example, create a +custom implementation of `~letsencrypt.interfaces.IAuthenticator` or +the `~letsencrypt.interfaces.IInstaller` without having to merge it +with the core upstream source code. An example is provided in +``examples/plugins/`` directory. + +.. warning:: Please be aware though that as this client is still in a + developer-preview stage, the API may undergo a few changes. If you + believe the plugin will be beneficial to the community, please + consider submitting a pull request to the repo and we will update + it with any necessary API changes. + +.. _`setuptools entry points`: + https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins + + .. _coding-style: Coding style diff --git a/docs/index.rst b/docs/index.rst index a7df1fb0f..68289d760 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,7 +8,6 @@ Welcome to the Let's Encrypt client documentation! using contributing packaging - plugins .. toctree:: :maxdepth: 1 diff --git a/docs/plugins.rst b/docs/plugins.rst deleted file mode 100644 index 33d63e84f..000000000 --- a/docs/plugins.rst +++ /dev/null @@ -1,19 +0,0 @@ -======= -Plugins -======= - -Let's Encrypt client supports dynamic discovery of plugins through the -`setuptools entry points`_. This way you can, for example, create a -custom implementation of `~letsencrypt.interfaces.IAuthenticator` or -the `~letsencrypt.interfaces.IInstaller` without having to merge it -with the core upstream source code. An example is provided in -``examples/plugins/`` directory. - -.. warning:: Please be aware though that as this client is still in a - developer-preview stage, the API may undergo a few changes. If you - believe the plugin will be beneficial to the community, please - consider submitting a pull request to the repo and we will update - it with any necessary API changes. - -.. _`setuptools entry points`: - https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins From 0233f2e9ac233dc9607e1c30372eb382c9213a4b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:37:25 +0000 Subject: [PATCH 094/216] docs headers: User/Developer/Packaging Guide --- docs/contributing.rst | 8 +++----- docs/packaging.rst | 6 +++--- docs/using.rst | 8 +++----- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 9166d7d8e..d0ce2626c 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -1,8 +1,6 @@ -============ -Contributing -============ - -Welcome to the Developer's Guide! +=============== +Developer Guide +=============== .. contents:: Table of Contents :local: diff --git a/docs/packaging.rst b/docs/packaging.rst index 84d7aa4ca..5f09b65fa 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -1,6 +1,6 @@ -========= -Packaging -========= +=============== +Packaging Guide +=============== Documentation can be found at https://github.com/letsencrypt/letsencrypt/wiki/Packaging. diff --git a/docs/using.rst b/docs/using.rst index 74c80887b..cbbce7687 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -1,8 +1,6 @@ -============================== -Using the Let's Encrypt client -============================== - -Welcome to the User Guide! +========== +User Guide +========== .. contents:: Table of Contents :local: From cf26d014cde5654b864b02c617d7fe3f4eba8430 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:40:20 +0000 Subject: [PATCH 095/216] {dev,user,packaging}.rst rename --- CONTRIBUTING.md | 2 +- README.rst | 2 +- bootstrap/install-deps.sh | 2 +- docs/{contributing.rst => dev.rst} | 0 docs/index.rst | 4 ++-- docs/{using.rst => user.rst} | 2 +- letsencrypt-auto | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename docs/{contributing.rst => dev.rst} (100%) rename docs/{using.rst => user.rst} (98%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bf19b18e1..ac08f0f4e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,4 +15,4 @@ to the Sphinx generated docs is provided below. --> -https://letsencrypt.readthedocs.org/en/latest/contributing.html +https://letsencrypt.readthedocs.org/en/latest/dev.html diff --git a/README.rst b/README.rst index 769bc7a8f..c0d663fe9 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,7 @@ server automatically!:: :alt: Docker Repository on Quay.io .. _`installation instructions`: - https://letsencrypt.readthedocs.org/en/latest/using.html + https://letsencrypt.readthedocs.org/en/latest/user.html .. _watch demo video: https://www.youtube.com/watch?v=Gas_sSB-5SU diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh index c159858c5..4785a841f 100755 --- a/bootstrap/install-deps.sh +++ b/bootstrap/install-deps.sh @@ -34,7 +34,7 @@ else echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run a pip install manually" - echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "Please see https://letsencrypt.readthedocs.org/en/latest/dev.html#prerequisites" echo "for more info" exit 1 fi diff --git a/docs/contributing.rst b/docs/dev.rst similarity index 100% rename from docs/contributing.rst rename to docs/dev.rst diff --git a/docs/index.rst b/docs/index.rst index 68289d760..9c4c40807 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,8 +5,8 @@ Welcome to the Let's Encrypt client documentation! :maxdepth: 2 intro - using - contributing + user + dev packaging .. toctree:: diff --git a/docs/using.rst b/docs/user.rst similarity index 98% rename from docs/using.rst rename to docs/user.rst index cbbce7687..9fd4f4e75 100644 --- a/docs/using.rst +++ b/docs/user.rst @@ -88,7 +88,7 @@ From source ----------- Installation from source is only supported for developers and the -whole process is described in :doc:`contributing`. +whole process is described in :doc:`dev`. .. warning:: Please do **not** use ``python setup.py install`` or ``python pip install .``. Please do **not** attempt the diff --git a/letsencrypt-auto b/letsencrypt-auto index 5b974c1f8..8df5eda81 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -55,7 +55,7 @@ then echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run a pip install manually" - echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "Please see https://letsencrypt.readthedocs.org/en/latest/dev.html#prerequisites" echo "for more info" fi From 6ef9f89c99dfa1b39cdd78bff8a429ae20656c02 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 19:52:54 +0000 Subject: [PATCH 096/216] Add plugins section to user guide --- docs/dev.rst | 1 + docs/user.rst | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/docs/dev.rst b/docs/dev.rst index d0ce2626c..5f2a84605 100644 --- a/docs/dev.rst +++ b/docs/dev.rst @@ -211,6 +211,7 @@ We currently offer a pythondialog and "text" mode for displays. Display plugins implement the `~letsencrypt.interfaces.IDisplay` interface. +.. _dev-plugin: Writing your own plugin ======================= diff --git a/docs/user.rst b/docs/user.rst index 9fd4f4e75..fa4008e8c 100644 --- a/docs/user.rst +++ b/docs/user.rst @@ -99,6 +99,15 @@ whole process is described in :doc:`dev`. Let's Encrypt team! +Plugins +======= + +Third party plugins are listed at +https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If that +that's not enough, you can always :ref:`write your own plugin +`. + + Configuration file ================== From dd254c3a9b2ac1dbb2a4db51679133901ccadfc6 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 20:20:35 +0000 Subject: [PATCH 097/216] Add plugins table to docs --- docs/user.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/user.rst b/docs/user.rst index fa4008e8c..81b2320c4 100644 --- a/docs/user.rst +++ b/docs/user.rst @@ -102,6 +102,27 @@ whole process is described in :doc:`dev`. Plugins ======= +Officially supported plugins: + +========== = = ================================================================ +Plugin A I Notes and status +========== = = ================================================================ +standalone Y N Very stable. Uses port 80 (force by + ``--standalone-supported-challenges simpleHttp``) or 443 + (force by ``statndalone-supported-challenges dvsni``). +webroot Y N Works with already running webserver, by writing necessary files + to the disk (``--webroot-path`` should be pointed to your + ``public_html``). Currently, when multiple domains are specified + (`-d`), they must all use the same web root path. +manual Y N Hidden from standard UI, use with ``--a manual``. Requires to + copy and paste commands into a new terminal session. Allows to + run client on machine different than target webserver, e.g. your + laptop. +apache Y Y Alpha - might break stuff, so be careful. Support for + Debian-derived distros only. +nginx Y Y Very experimental. Not included in letsencrypt-auto_. +========== = = ================================================================ + Third party plugins are listed at https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If that that's not enough, you can always :ref:`write your own plugin From 4def79d306a0202bb0d1e6f09d9318c4fcea1720 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 24 Oct 2015 13:38:03 -0700 Subject: [PATCH 098/216] Display the Exception as well as the error message --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 2aedeea8f..ed452b5e6 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -352,7 +352,7 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): else: msg = ("The {0} plugin is not working; there may be problems with " "your existing configuration.\nThe error was: {1}" - .format(requested, plugins[requested].problem)) + .format(requested, `plugins[requested].problem`)) elif cfg_type == "installer": if os.path.exists("/etc/debian_version"): # Debian... installers are at least possible From 195f36e4e1114eaff98000f0dc404475ea09fb68 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 24 Oct 2015 14:09:34 -0700 Subject: [PATCH 099/216] plugin_ep.problem unittests and some misc style things --- letsencrypt/cli.py | 4 ++-- letsencrypt/plugins/disco.py | 1 + letsencrypt/plugins/disco_test.py | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index ed452b5e6..3b16d0b6a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -351,8 +351,8 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): msg = "The requested {0} plugin does not appear to be installed".format(requested) else: msg = ("The {0} plugin is not working; there may be problems with " - "your existing configuration.\nThe error was: {1}" - .format(requested, `plugins[requested].problem`)) + "your existing configuration.\nThe error was: {1!r}" + .format(requested, plugins[requested].problem)) elif cfg_type == "installer": if os.path.exists("/etc/debian_version"): # Debian... installers are at least possible diff --git a/letsencrypt/plugins/disco.py b/letsencrypt/plugins/disco.py index 3f26206c7..9ed6ac596 100644 --- a/letsencrypt/plugins/disco.py +++ b/letsencrypt/plugins/disco.py @@ -125,6 +125,7 @@ class PluginEntryPoint(object): """Return the Exception raised during plugin setup, or None if all is well""" if isinstance(self._prepared, Exception): return self._prepared + return None @property def available(self): diff --git a/letsencrypt/plugins/disco_test.py b/letsencrypt/plugins/disco_test.py index 8660d94a1..88c7a671f 100644 --- a/letsencrypt/plugins/disco_test.py +++ b/letsencrypt/plugins/disco_test.py @@ -68,6 +68,7 @@ class PluginEntryPointTest(unittest.TestCase): self.assertFalse(self.plugin_ep.prepared) self.assertFalse(self.plugin_ep.misconfigured) self.assertFalse(self.plugin_ep.available) + self.assertTrue(self.plugin_ep.problem is None) self.assertTrue(self.plugin_ep.entry_point is EP_SA) self.assertEqual("sa", self.plugin_ep.name) @@ -131,6 +132,8 @@ class PluginEntryPointTest(unittest.TestCase): errors.MisconfigurationError)) self.assertTrue(self.plugin_ep.prepared) self.assertTrue(self.plugin_ep.misconfigured) + self.assertTrue(isinstance(self.plugin_ep.problem, + errors.MisconfigurationError)) self.assertTrue(self.plugin_ep.available) def test_prepare_no_installation(self): From eee217acd307014347a53b94c6d3b15e93169ab2 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 21:23:47 +0000 Subject: [PATCH 100/216] Fix link to letsencrypt-auto --- docs/user.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user.rst b/docs/user.rst index 81b2320c4..3af49f259 100644 --- a/docs/user.rst +++ b/docs/user.rst @@ -14,7 +14,7 @@ the letsencrypt-auto_ method described below. It's the fastest, the most thourougly tested and the most reliable way of getting our software and the free SSL certificates! -.. letsencrypt-auto: +.. _letsencrypt-auto: letsencrypt-auto ---------------- From 007969151be45e004d3e13dd9eac778bd9832a59 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 24 Oct 2015 21:24:09 +0000 Subject: [PATCH 101/216] Add "Getting help" section to User Guide --- docs/user.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/user.rst b/docs/user.rst index 3af49f259..913a3de1f 100644 --- a/docs/user.rst +++ b/docs/user.rst @@ -5,6 +5,7 @@ User Guide .. contents:: Table of Contents :local: +.. _installation: Installation ============ @@ -154,3 +155,25 @@ By default, the following locations are searched: .. [#venv] By using this virtualized Python enviroment (`virtualenv `_) we don't pollute the main OS space with packages from PyPI! + + +Getting help +============ + +If you're having problems you can chat with us on `IRC (#letsencrypt @ +Freenode) `_ or +get support on our `forums `_. + +If you find a bug in the software, please do report it in our `issue +tracker +`_. Remember to +give us us as much information as possible: + +- copy and paste exact command line used and the output (though mind + that the latter might include some personally identifiable + information, including your email and domains) +- copy and paste logs from ``/var/log/letsencrypt`` (though mind they + also might contain personally identifiable information) +- copy and paste ``letsencrypt --version`` output +- your operating system, including specific version +- specify which installation_ method you've chosen From e8f90ff9939b018f40444490bdff87ef38f62415 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 24 Oct 2015 23:59:36 +0100 Subject: [PATCH 102/216] Add flag in to _common.sh integration test --- tests/integration/_common.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index 1dffaa4e3..bdd73244a 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -20,6 +20,7 @@ letsencrypt_test () { --manual-test-mode \ $store_flags \ --text \ + --redirect \ --agree-dev-preview \ --agree-tos \ --email "" \ From 1f6cc52e6696102d0585a4d2a82071edfd4ceaa5 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2015 00:07:00 +0100 Subject: [PATCH 103/216] Fix integration test bug --- tests/integration/_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index bdd73244a..db514134d 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -20,7 +20,7 @@ letsencrypt_test () { --manual-test-mode \ $store_flags \ --text \ - --redirect \ + -r \ --agree-dev-preview \ --agree-tos \ --email "" \ From b13006ce1af7b2d1f2c44d851de8d17698643c77 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 25 Oct 2015 00:14:46 +0100 Subject: [PATCH 104/216] Change -n flag in _commit.py to --no-redirect for clarity. --- tests/integration/_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index db514134d..07eca44b2 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -20,7 +20,7 @@ letsencrypt_test () { --manual-test-mode \ $store_flags \ --text \ - -r \ + --no-redirect \ --agree-dev-preview \ --agree-tos \ --email "" \ From a966bc0797b9a9fa3d6aaa0a5a81d61257b49dc0 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sat, 24 Oct 2015 21:27:49 -0500 Subject: [PATCH 105/216] letsencrypt.plugins.manual: Add disclaimer about IP logging --- letsencrypt/plugins/manual.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index f7717064b..d722b8e36 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -46,6 +46,15 @@ If you don't have HTTP server configured, you can run the following command on the target server (as root): {command} +""" + + # a disclaimer about your current IP being transmitted to Let's Encrypt's servers. + IP_DISCLAIMER = """\ +NOTE: The IP of this machine will be publicly logged as having requested this certificate. \ +If you're running letsencrypt in manual mode on a machine that is not your server, \ +please ensure you're okay with that. + +Are you OK with your IP being logged? """ # "cd /tmp/letsencrypt" makes sure user doesn't serve /root, @@ -151,6 +160,10 @@ binary for temporary key/certificate generation.""".replace("\n", "") if self._httpd.poll() is not None: raise errors.Error("Couldn't execute manual command") else: + if not zope.component.getUtility(interfaces.IDisplay).yesno( + self.IP_DISCLAIMER, "Yes", "No"): + raise errors.Error("Must agree to proceed") + self._notify_and_wait(self.MESSAGE_TEMPLATE.format( validation=validation.json_dumps(), response=response, uri=response.uri(achall.domain, achall.challb.chall), From 4adc6d32692e6d2e2e70b81a8f7ec5865ed687a3 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sat, 24 Oct 2015 21:50:27 -0500 Subject: [PATCH 106/216] Make pep8 happy --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index d722b8e36..cdbc97aa0 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -161,7 +161,7 @@ binary for temporary key/certificate generation.""".replace("\n", "") raise errors.Error("Couldn't execute manual command") else: if not zope.component.getUtility(interfaces.IDisplay).yesno( - self.IP_DISCLAIMER, "Yes", "No"): + self.IP_DISCLAIMER, "Yes", "No"): raise errors.Error("Must agree to proceed") self._notify_and_wait(self.MESSAGE_TEMPLATE.format( From aa0c7d993256fe063cbf1254c5d4ed215902f3ac Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sat, 24 Oct 2015 22:03:30 -0500 Subject: [PATCH 107/216] manual_test: mock yesno interaction --- letsencrypt/plugins/manual_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 8cfff1cc5..d5266d711 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -43,11 +43,13 @@ class AuthenticatorTest(unittest.TestCase): def test_perform_empty(self): self.assertEqual([], self.auth.perform([])) + @mock.patch("letsencrypt.proof_of_possession.zope.component.getUtility") @mock.patch("letsencrypt.plugins.manual.sys.stdout") @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") @mock.patch("__builtin__.raw_input") - def test_perform(self, mock_raw_input, mock_verify, mock_stdout): + def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_input, mock_interaction): mock_verify.return_value = True + mock_interaction.yesno.return_value = True resp = challenges.SimpleHTTPResponse(tls=False) self.assertEqual([resp], self.auth.perform(self.achalls)) From 2d295bce9d6324c34e1034640399ca714a84ec06 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sat, 24 Oct 2015 22:17:16 -0500 Subject: [PATCH 108/216] Better error message --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index cdbc97aa0..6798c9c92 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -162,7 +162,7 @@ binary for temporary key/certificate generation.""".replace("\n", "") else: if not zope.component.getUtility(interfaces.IDisplay).yesno( self.IP_DISCLAIMER, "Yes", "No"): - raise errors.Error("Must agree to proceed") + raise errors.Error("Must agree to IP logging to proceed") self._notify_and_wait(self.MESSAGE_TEMPLATE.format( validation=validation.json_dumps(), response=response, From a45c4d157aa9f120025b1d133eca612f7c83fee7 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sat, 24 Oct 2015 23:27:39 -0500 Subject: [PATCH 109/216] Oops, copy-pasted the patch --- letsencrypt/plugins/manual_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index d5266d711..617bad958 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -43,11 +43,11 @@ class AuthenticatorTest(unittest.TestCase): def test_perform_empty(self): self.assertEqual([], self.auth.perform([])) - @mock.patch("letsencrypt.proof_of_possession.zope.component.getUtility") + @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") @mock.patch("letsencrypt.plugins.manual.sys.stdout") @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") @mock.patch("__builtin__.raw_input") - def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_input, mock_interaction): + def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): mock_verify.return_value = True mock_interaction.yesno.return_value = True From ae9e5f7fff94741e52a3f183b940fad4e7f762b5 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 10:14:40 +0000 Subject: [PATCH 110/216] Revert to {using,contributing}.rst --- CONTRIBUTING.md | 2 +- README.rst | 2 +- bootstrap/install-deps.sh | 2 +- docs/{dev.rst => contributing.rst} | 0 docs/index.rst | 4 ++-- docs/{user.rst => using.rst} | 2 +- letsencrypt-auto | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename docs/{dev.rst => contributing.rst} (100%) rename docs/{user.rst => using.rst} (99%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ac08f0f4e..bf19b18e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,4 +15,4 @@ to the Sphinx generated docs is provided below. --> -https://letsencrypt.readthedocs.org/en/latest/dev.html +https://letsencrypt.readthedocs.org/en/latest/contributing.html diff --git a/README.rst b/README.rst index c0d663fe9..769bc7a8f 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,7 @@ server automatically!:: :alt: Docker Repository on Quay.io .. _`installation instructions`: - https://letsencrypt.readthedocs.org/en/latest/user.html + https://letsencrypt.readthedocs.org/en/latest/using.html .. _watch demo video: https://www.youtube.com/watch?v=Gas_sSB-5SU diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh index 4785a841f..c159858c5 100755 --- a/bootstrap/install-deps.sh +++ b/bootstrap/install-deps.sh @@ -34,7 +34,7 @@ else echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run a pip install manually" - echo "Please see https://letsencrypt.readthedocs.org/en/latest/dev.html#prerequisites" + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" echo "for more info" exit 1 fi diff --git a/docs/dev.rst b/docs/contributing.rst similarity index 100% rename from docs/dev.rst rename to docs/contributing.rst diff --git a/docs/index.rst b/docs/index.rst index 9c4c40807..68289d760 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,8 +5,8 @@ Welcome to the Let's Encrypt client documentation! :maxdepth: 2 intro - user - dev + using + contributing packaging .. toctree:: diff --git a/docs/user.rst b/docs/using.rst similarity index 99% rename from docs/user.rst rename to docs/using.rst index 913a3de1f..9632c17d1 100644 --- a/docs/user.rst +++ b/docs/using.rst @@ -89,7 +89,7 @@ From source ----------- Installation from source is only supported for developers and the -whole process is described in :doc:`dev`. +whole process is described in :doc:`contributing`. .. warning:: Please do **not** use ``python setup.py install`` or ``python pip install .``. Please do **not** attempt the diff --git a/letsencrypt-auto b/letsencrypt-auto index 8df5eda81..5b974c1f8 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -55,7 +55,7 @@ then echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run a pip install manually" - echo "Please see https://letsencrypt.readthedocs.org/en/latest/dev.html#prerequisites" + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" echo "for more info" fi From 6b69095c8b6a54b04159312567f2e2d8028c1ad2 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 10:27:09 +0000 Subject: [PATCH 111/216] Move footnotes to the bottom --- docs/using.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 9632c17d1..951f7875f 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -150,13 +150,6 @@ By default, the following locations are searched: .. keep it up to date with constants.py -.. rubric:: Footnotes - -.. [#venv] By using this virtualized Python enviroment (`virtualenv - `_) we don't pollute the main - OS space with packages from PyPI! - - Getting help ============ @@ -177,3 +170,10 @@ give us us as much information as possible: - copy and paste ``letsencrypt --version`` output - your operating system, including specific version - specify which installation_ method you've chosen + + +.. rubric:: Footnotes + +.. [#venv] By using this virtualized Python enviroment (`virtualenv + `_) we don't pollute the main + OS space with packages from PyPI! From a2d5527c5a1d514b87cd9951a24ca3bcfd157a68 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 10:28:55 +0000 Subject: [PATCH 112/216] *the* X Guide --- docs/using.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 951f7875f..794b8866a 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -82,14 +82,14 @@ Distro packages Unfortunately, this is an ongoing effort. If you'd like to package Let's Encrypt client for your distribution of choice please have a -look at :doc:`packaging`. +look at the :doc:`packaging`. From source ----------- Installation from source is only supported for developers and the -whole process is described in :doc:`contributing`. +whole process is described in the :doc:`contributing`. .. warning:: Please do **not** use ``python setup.py install`` or ``python pip install .``. Please do **not** attempt the From d456771698add31fcfaddfffbec5eb310e97da4b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 11:01:20 +0000 Subject: [PATCH 113/216] Fix typos --- docs/using.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 794b8866a..f747fa389 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -12,7 +12,7 @@ Installation Unless you have a very specific requirements, we kindly ask you to use the letsencrypt-auto_ method described below. It's the fastest, the -most thourougly tested and the most reliable way of getting our +most thoroughly tested and the most reliable way of getting our software and the free SSL certificates! .. _letsencrypt-auto: @@ -21,8 +21,8 @@ letsencrypt-auto ---------------- ``letsencrypt-auto`` is a wrapper which installs some dependencies -from your OS standard package repostories (e.g using `apt-get` or -`yum`), and for other depencies it sets up a virtualized Python +from your OS standard package repositories (e.g using `apt-get` or +`yum`), and for other dependencies it sets up a virtualized Python environment with packages downloaded from PyPI [#venv]_. It also provides automated updates. @@ -48,7 +48,7 @@ To install and run the client you just need to type: ./letsencrypt-auto Throughout the documentation, whenever you see references to -``letsencrypt`` script/binary, you can subsitute in +``letsencrypt`` script/binary, you can substitute in ``letsencrypt-auto``. For example, to get the help you would type: .. code-block:: shell @@ -110,7 +110,7 @@ Plugin A I Notes and status ========== = = ================================================================ standalone Y N Very stable. Uses port 80 (force by ``--standalone-supported-challenges simpleHttp``) or 443 - (force by ``statndalone-supported-challenges dvsni``). + (force by ``standalone-supported-challenges dvsni``). webroot Y N Works with already running webserver, by writing necessary files to the disk (``--webroot-path`` should be pointed to your ``public_html``). Currently, when multiple domains are specified @@ -174,6 +174,6 @@ give us us as much information as possible: .. rubric:: Footnotes -.. [#venv] By using this virtualized Python enviroment (`virtualenv +.. [#venv] By using this virtualized Python environment (`virtualenv `_) we don't pollute the main OS space with packages from PyPI! From ce4452d65b15d58fff14b1862184b4193dd414d7 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 11:30:46 +0000 Subject: [PATCH 114/216] Add "Where are my certificates?" to docs (fixes #608). --- docs/using.rst | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/docs/using.rst b/docs/using.rst index f747fa389..9db616387 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -130,6 +130,73 @@ that's not enough, you can always :ref:`write your own plugin `. +Where are my certificates? +========================== + +First of all, we encourage you to use Apache or nginx installers, both +which perform the certificate managemant automatically. If, however, +you prefer to manage everything by hand, this section provides +information on where to find necessary files. + +All generated keys and issued certificates can be found in +``/etc/letsencrypt/live/$domain``. Rather than copying, please point +your (web) server configuration directly to those files (or create +symlinks). During the renewal_, ``/etc/letsencrypt/live`` is updated +with the latest necessary files. + +.. note:: ``/etc/letsencrypt/archive`` and ``/etc/letsencrypt/keys`` + contain all previous keys and certificates, while + ``/etc/letsencrypt/live`` symlinks to the latest versions. + +The following files are available: + +``privkey.pem`` + Private key for the certificate. + + .. warning:: This **must be kept secret at all times**! Never share + it with anyone, including Let's Encrypt developers. You cannot + put it into safe, however - your server still needs to access + this file in order for SSL/TLS to work. + + This is what Apache needs for `SSLCertificateKeyFile + `_, + and nginx for `ssl_certificate_key + `_. + +``cert.pem`` + Server certificate only. + + This is what Apache needs for `SSLCertificateFile + `_. + +``chain.pem`` + All certificates that need to be served by the browser **excluding** + server certificate, i.e. root and intermediate certificates only. + + This is what Apache needs for `SSLCertificateChainFile + `_. + +``fullchain.pem`` + All certificates, **including** server certificate. This is + concatenation of ``chain.pem`` and ``cert.pem``. + + This is what nginx needs for `ssl_certificate + `_. + + +For both chain files, all certificates are ordered from root (primary +certificate) towards leaf. + +Please note, that **you must use** either ``chain.pem`` or +``fullchain.pem``. In case of webservers, using only ``cert.pem``, +will cause nasty errors served through the browsers! + +.. note:: All files are PEM-encoded (as the filename suffix + suggests). If you need other format, such as DER or PFX, then you + could convert using ``openssl``, but this means you will not + benefit from automatic renewal_! + + Configuration file ================== From 38036b758c77b82ee64d1195ea70391ad3947d41 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 12:15:03 +0000 Subject: [PATCH 115/216] Add basic docs on renewal (fixes #951). --- docs/using.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/using.rst b/docs/using.rst index 9db616387..04b0a7432 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -130,6 +130,25 @@ that's not enough, you can always :ref:`write your own plugin `. +Renewal +======= + +.. note:: Let's Encrypt CA issues short lived certificates (90 + days). Make sure you renew the certificates at least once in 3 + months. + +In order to renew certificates simply call the ``letsencrypt`` (or +letsencrypt-auto_) again, and use the same values when prompted. You +can automate it slightly by passing necessary flags on the CLI (see +`--help all`), or even further using the :ref:`config-file`. If you're +sure that UI doesn't prompt for any details you can add the command to +``crontab`` (make it less than every 90 days to avoid problems, say +every month). + +Let's Encrypt is working hard on automating the renewal process. Until +the tool is ready, we are sorry for the inconvenience! + + Where are my certificates? ========================== @@ -197,6 +216,8 @@ will cause nasty errors served through the browsers! benefit from automatic renewal_! +.. _config-file: + Configuration file ================== From c43825fd35f9d8b7b3de24db50396c1756047538 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 12:35:26 +0000 Subject: [PATCH 116/216] Explain Docker limitations (fixes #1000). --- docs/using.rst | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 04b0a7432..a1029351d 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -59,9 +59,22 @@ Throughout the documentation, whenever you see references to Running with Docker ------------------- -Docker_ is another way to quickly obtain testing certs. From the -server that the domain your requesting a cert for resolves to, -`install Docker`_, issue the following command: +Docker_ is an amazingly simple and quick way to obtain a +certificate. However, this mode of operation is unable to install +certificates or configure your webserver, because our installer +plugins cannot reach it from inside the Docker container. + +You should definitely read the :ref:`where-certs` section, in order to +know how to manage the certs +manually. https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance +provides some information about recommended ciphersuites. If none of +these make much sense to you, you should definitely use the +letsencrypt-auto_ method, which enables you to use installer plugins +that cover both of those hard topics. + +If you're still not convinced and have decided to use this method, +from the server that the domain your requesting a cert for resolves +to, `install Docker`_, issue the following command: .. code-block:: shell @@ -70,8 +83,9 @@ server that the domain your requesting a cert for resolves to, -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ quay.io/letsencrypt/letsencrypt:latest auth -and follow the instructions. Your new cert will be available in -``/etc/letsencrypt/certs``. +and follow the instructions (note that ``auth`` command is explicitly +used - no installer plugins involved). Your new cert will be available +in ``/etc/letsencrypt/live`` on the host. .. _Docker: https://docker.com .. _`install Docker`: https://docs.docker.com/userguide/ @@ -149,6 +163,8 @@ Let's Encrypt is working hard on automating the renewal process. Until the tool is ready, we are sorry for the inconvenience! +.. _where-certs: + Where are my certificates? ========================== From 27a35a5915d7b35eb1cbd47f2d4c9f051092bce5 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 13:04:49 +0000 Subject: [PATCH 117/216] Remove confusing short flags --- letsencrypt/cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 8da015382..a25eca11f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -834,14 +834,14 @@ def prepare_and_parse_args(plugins, args): helpful.add_group( "security", description="Security parameters & server settings") helpful.add( - "security", "-B", "--rsa-key-size", type=int, metavar="N", + "security", "--rsa-key-size", type=int, metavar="N", default=flag_default("rsa_key_size"), help=config_help("rsa_key_size")) helpful.add( - "security", "-r", "--redirect", action="store_true", + "security", "--redirect", action="store_true", help="Automatically redirect all HTTP traffic to HTTPS for the newly " "authenticated vhost.", dest="redirect", default=None) helpful.add( - "security", "-n", "--no-redirect", action="store_false", + "security", "--no-redirect", action="store_false", help="Do not automatically redirect all HTTP traffic to HTTPS for the newly " "authenticated vhost.", dest="redirect", default=None) helpful.add( From aefd7ac7fd159be58f36cbb60c5865d2cd094e64 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 25 Oct 2015 13:05:49 +0000 Subject: [PATCH 118/216] Remove old todo (#479 is fixed) --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 8da015382..4a33a282e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -825,7 +825,7 @@ def prepare_and_parse_args(plugins, args): "testing", "--no-verify-ssl", action="store_true", help=config_help("no_verify_ssl"), default=flag_default("no_verify_ssl")) - helpful.add( # TODO: apache plugin does NOT respect it (#479) + helpful.add( "testing", "--dvsni-port", type=int, default=flag_default("dvsni_port"), help=config_help("dvsni_port")) helpful.add("testing", "--simple-http-port", type=int, From a88f9cdc374783adb8f42e4fd09d99559f98f055 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 14:31:30 -0500 Subject: [PATCH 119/216] Switch from Error to PluginError --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 6798c9c92..61a5acdb3 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -162,7 +162,7 @@ binary for temporary key/certificate generation.""".replace("\n", "") else: if not zope.component.getUtility(interfaces.IDisplay).yesno( self.IP_DISCLAIMER, "Yes", "No"): - raise errors.Error("Must agree to IP logging to proceed") + raise errors.PluginError("Must agree to IP logging to proceed") self._notify_and_wait(self.MESSAGE_TEMPLATE.format( validation=validation.json_dumps(), response=response, From a21e149f74466f9cb56509071099b7b56e97a308 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 14:54:39 -0500 Subject: [PATCH 120/216] Need to *call* mock_interaction as a function --- letsencrypt/plugins/manual_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 617bad958..7a75c02b5 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -49,7 +49,7 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("__builtin__.raw_input") def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): mock_verify.return_value = True - mock_interaction.yesno.return_value = True + mock_interaction().yesno.return_value = True resp = challenges.SimpleHTTPResponse(tls=False) self.assertEqual([resp], self.auth.perform(self.achalls)) From 7e94876f73d069375b10c56701fdb24bf6ef7988 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 15:04:26 -0500 Subject: [PATCH 121/216] Add a test for disagreeing with IP logging --- letsencrypt/plugins/manual_test.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 7a75c02b5..9476e2316 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -106,6 +106,18 @@ class AuthenticatorTest(unittest.TestCase): self.auth_test_mode.cleanup(self.achalls) mock_killpg.assert_called_once_with(1234, signal.SIGTERM) + @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") + @mock.patch("letsencrypt.plugins.manual.sys.stdout") + @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") + @mock.patch("__builtin__.raw_input") + def test_disagree_with_ip_logging(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): + mock_verify.return_value = True + mock_interaction().yesno.return_value = False + + resp = challenges.SimpleHTTPResponse(tls=False) + with self.assertRaises(errors.PluginError): + self.auth.perform(self.achalls) + if __name__ == "__main__": unittest.main() # pragma: no cover From 95a6b8cbadbc2aa60bcaac4cf97729bf352a15d3 Mon Sep 17 00:00:00 2001 From: Damian Duesentrieb Date: Sun, 25 Oct 2015 21:28:19 +0100 Subject: [PATCH 122/216] Added support for Manjaro Linux in install-script --- bootstrap/manjaro.sh | 19 +++++++++++++++++++ letsencrypt-auto | 3 +++ 2 files changed, 22 insertions(+) create mode 100755 bootstrap/manjaro.sh diff --git a/bootstrap/manjaro.sh b/bootstrap/manjaro.sh new file mode 100755 index 000000000..18564cac9 --- /dev/null +++ b/bootstrap/manjaro.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# Manjaros package-management is very similiar to Arch-Linux. +# Therefore this script is currently identical to . +# This must not hold in future. + +# "python-virtualenv" is Python3, but "python2-virtualenv" provides +# only "virtualenv2" binary, not "virtualenv" necessary in +# ./bootstrap/dev/_common_venv.sh +pacman -S --needed \ + git \ + python2 \ + python-virtualenv \ + gcc \ + dialog \ + augeas \ + openssl \ + libffi \ + ca-certificates \ diff --git a/letsencrypt-auto b/letsencrypt-auto index 5b974c1f8..79d2b0501 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -41,6 +41,9 @@ then elif [ -f /etc/arch-release ] ; then echo "Bootstrapping dependencies for Archlinux..." $SUDO $BOOTSTRAP/archlinux.sh + elif [ -f /etc/manjaro-release ] ; then + echo "Bootstrapping dependencies for Manjaro linux..." + $SUDO $BOOTSTRAP/archlinux.sh elif [ -f /etc/redhat-release ] ; then echo "Bootstrapping dependencies for RedHat-based OSes..." $SUDO $BOOTSTRAP/_rpm_common.sh From 17bd3790177f0cd8fda2009f295be14b61c78cdd Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 15:32:32 -0500 Subject: [PATCH 123/216] Clean up tests --- letsencrypt/plugins/manual_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 9476e2316..d3b880e47 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -110,11 +110,11 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("letsencrypt.plugins.manual.sys.stdout") @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") @mock.patch("__builtin__.raw_input") - def test_disagree_with_ip_logging(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): - mock_verify.return_value = True + def test_disagree_with_ip_logging(self, mock_raw_input, mock_verify, mock_stdout, + mock_interaction): + # pylint: disable=unused-argument mock_interaction().yesno.return_value = False - resp = challenges.SimpleHTTPResponse(tls=False) with self.assertRaises(errors.PluginError): self.auth.perform(self.achalls) From 88cc70b5a526b2ce7666135f3dc0b5844bebbe94 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 15:46:21 -0500 Subject: [PATCH 124/216] Oops, can't use the form of assertRaises on 2.6 --- letsencrypt/plugins/manual_test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index d3b880e47..77871a51e 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -115,8 +115,7 @@ class AuthenticatorTest(unittest.TestCase): # pylint: disable=unused-argument mock_interaction().yesno.return_value = False - with self.assertRaises(errors.PluginError): - self.auth.perform(self.achalls) + self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) if __name__ == "__main__": From 0a5303ccf0660d9984e6c1ebe457c123947164e0 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 16:15:10 -0500 Subject: [PATCH 125/216] test_disagree_with_ip_logging: move, remove spurious @patches --- letsencrypt/plugins/manual_test.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 77871a51e..673942799 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -63,6 +63,12 @@ class AuthenticatorTest(unittest.TestCase): mock_verify.return_value = False self.assertEqual([None], self.auth.perform(self.achalls)) + @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") + def test_disagree_with_ip_logging(self, mock_interaction): + mock_interaction().yesno.return_value = False + + self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) + @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) def test_perform_test_command_oserror(self, mock_popen): mock_popen.side_effect = OSError @@ -106,17 +112,6 @@ class AuthenticatorTest(unittest.TestCase): self.auth_test_mode.cleanup(self.achalls) mock_killpg.assert_called_once_with(1234, signal.SIGTERM) - @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") - @mock.patch("letsencrypt.plugins.manual.sys.stdout") - @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") - @mock.patch("__builtin__.raw_input") - def test_disagree_with_ip_logging(self, mock_raw_input, mock_verify, mock_stdout, - mock_interaction): - # pylint: disable=unused-argument - mock_interaction().yesno.return_value = False - - self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) - if __name__ == "__main__": unittest.main() # pragma: no cover From 49b6f37e77542c425a8bb81ddf57ccd1a153d7e2 Mon Sep 17 00:00:00 2001 From: Damian Duesentrieb Date: Sun, 25 Oct 2015 22:27:11 +0100 Subject: [PATCH 126/216] Support for Manjaro Linux --- bootstrap/_arch_common.sh | 19 +++++++++++++++++++ bootstrap/archlinux.sh | 16 +--------------- bootstrap/manjaro.sh | 1 + letsencrypt-auto | 3 +++ 4 files changed, 24 insertions(+), 15 deletions(-) create mode 100755 bootstrap/_arch_common.sh mode change 100755 => 120000 bootstrap/archlinux.sh create mode 120000 bootstrap/manjaro.sh diff --git a/bootstrap/_arch_common.sh b/bootstrap/_arch_common.sh new file mode 100755 index 000000000..da78ead01 --- /dev/null +++ b/bootstrap/_arch_common.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +# Tested with: +# - Manjaro 15.09 (x86_64) +# - ArchLinux (x86_64) + +# "python-virtualenv" is Python3, but "python2-virtualenv" provides +# only "virtualenv2" binary, not "virtualenv" necessary in +# ./bootstrap/dev/_common_venv.sh +pacman -S --needed \ + git \ + python2 \ + python-virtualenv \ + gcc \ + dialog \ + augeas \ + openssl \ + libffi \ + ca-certificates \ diff --git a/bootstrap/archlinux.sh b/bootstrap/archlinux.sh deleted file mode 100755 index 884262f0b..000000000 --- a/bootstrap/archlinux.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -# "python-virtualenv" is Python3, but "python2-virtualenv" provides -# only "virtualenv2" binary, not "virtualenv" necessary in -# ./bootstrap/dev/_common_venv.sh -pacman -S --needed \ - git \ - python2 \ - python-virtualenv \ - gcc \ - dialog \ - augeas \ - openssl \ - libffi \ - ca-certificates \ diff --git a/bootstrap/archlinux.sh b/bootstrap/archlinux.sh new file mode 120000 index 000000000..c5c9479f7 --- /dev/null +++ b/bootstrap/archlinux.sh @@ -0,0 +1 @@ +_arch_common.sh \ No newline at end of file diff --git a/bootstrap/manjaro.sh b/bootstrap/manjaro.sh new file mode 120000 index 000000000..c5c9479f7 --- /dev/null +++ b/bootstrap/manjaro.sh @@ -0,0 +1 @@ +_arch_common.sh \ No newline at end of file diff --git a/letsencrypt-auto b/letsencrypt-auto index 5b974c1f8..769f5a1d9 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -41,6 +41,9 @@ then elif [ -f /etc/arch-release ] ; then echo "Bootstrapping dependencies for Archlinux..." $SUDO $BOOTSTRAP/archlinux.sh + elif [ -f /etc/manjaro-release ] ; then + echo "Bootstrapping dependencies for Manjaro Linux..." + $SUDO $BOOTSTRAP/manjaro.sh elif [ -f /etc/redhat-release ] ; then echo "Bootstrapping dependencies for RedHat-based OSes..." $SUDO $BOOTSTRAP/_rpm_common.sh From 3f22e0d6f2b89f6ee3d36f882b0de2b58d937880 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 16:32:56 -0500 Subject: [PATCH 127/216] test_disagree_with_ip_logging: make it fail nicely --- letsencrypt/plugins/manual_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 673942799..e04ff3594 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -64,8 +64,10 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual([None], self.auth.perform(self.achalls)) @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") - def test_disagree_with_ip_logging(self, mock_interaction): + @mock.patch("letsencrypt.plugins.manual.Authenticator._notify_and_wait") + def test_disagree_with_ip_logging(self, mock_notify, mock_interaction): mock_interaction().yesno.return_value = False + mock_notify.side_effect = errors.Error("Exception not raised, continued execution even after disagreeing with IP logging") self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) From a90013bc2df6129771bc0dd1f75ed18be2c89049 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 16:43:03 -0500 Subject: [PATCH 128/216] Make linter happy --- letsencrypt/plugins/manual_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index e04ff3594..a52129635 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -67,7 +67,8 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("letsencrypt.plugins.manual.Authenticator._notify_and_wait") def test_disagree_with_ip_logging(self, mock_notify, mock_interaction): mock_interaction().yesno.return_value = False - mock_notify.side_effect = errors.Error("Exception not raised, continued execution even after disagreeing with IP logging") + mock_notify.side_effect = errors.Error("Exception not raised, \ + continued execution even after disagreeing with IP logging") self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) From 335cf82f1366f3f554a386e082ca342ae452a872 Mon Sep 17 00:00:00 2001 From: Liam Marshall Date: Sun, 25 Oct 2015 18:06:25 -0500 Subject: [PATCH 129/216] Remove AAAA record check suggestion Boulder doesn't support ipv6 validation, so don't confuse people by asking them to check their AAAA record(s). --- letsencrypt/auth_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 2da053f8c..5f6365167 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -486,7 +486,7 @@ def is_preferred(offered_challb, satisfied, _ERROR_HELP_COMMON = ( "To fix these errors, please make sure that your domain name was entered " - "correctly and the DNS A/AAAA record(s) for that domain contains the " + "correctly and the DNS A record(s) for that domain contains the " "right IP address.") From ca777a300fca1351dace916307e0529632aa5501 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Mon, 26 Oct 2015 18:58:16 +0000 Subject: [PATCH 130/216] Comparison of different installation methods --- docs/using.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index a1029351d..73d0aea64 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -10,11 +10,6 @@ User Guide Installation ============ -Unless you have a very specific requirements, we kindly ask you to use -the letsencrypt-auto_ method described below. It's the fastest, the -most thoroughly tested and the most reliable way of getting our -software and the free SSL certificates! - .. _letsencrypt-auto: letsencrypt-auto @@ -114,6 +109,15 @@ whole process is described in the :doc:`contributing`. Let's Encrypt team! +Comparison of different methods +------------------------------- + +Unless you have a very specific requirements, we kindly ask you to use +the letsencrypt-auto_ method. It's the fastest, the most thoroughly +tested and the most reliable way of getting our software and the free +SSL certificates! + + Plugins ======= From bbea646078ed510b6849001478430f1b1024ecfc Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Mon, 26 Oct 2015 19:00:06 +0000 Subject: [PATCH 131/216] docs: better apache desc + move to 2nd place --- docs/using.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 73d0aea64..cf4d4fe90 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -129,6 +129,8 @@ Plugin A I Notes and status standalone Y N Very stable. Uses port 80 (force by ``--standalone-supported-challenges simpleHttp``) or 443 (force by ``standalone-supported-challenges dvsni``). +apache Y Y Alpha. Automates Apache installation, works fairly well but on + Debian-based distributions only for now. webroot Y N Works with already running webserver, by writing necessary files to the disk (``--webroot-path`` should be pointed to your ``public_html``). Currently, when multiple domains are specified @@ -137,8 +139,6 @@ manual Y N Hidden from standard UI, use with ``--a manual``. Requires to copy and paste commands into a new terminal session. Allows to run client on machine different than target webserver, e.g. your laptop. -apache Y Y Alpha - might break stuff, so be careful. Support for - Debian-derived distros only. nginx Y Y Very experimental. Not included in letsencrypt-auto_. ========== = = ================================================================ From 2cb3d494b62abc5c11493145b999d07dd2bd62a1 Mon Sep 17 00:00:00 2001 From: Daniel Albers Date: Mon, 26 Oct 2015 22:54:54 +0100 Subject: [PATCH 132/216] Fix typos --- docs/using.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 883efc78c..e2d4fcf6f 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -33,7 +33,7 @@ To install and run the client you just need to type: (Once letsencrypt is packaged by distributions, the command will just be ``letsencrypt``. ``letsencrypt-auto`` is a wrapper which installs virtualized -dependencies and provides automated updates during the beta program) +dependencies and provides automated updates during the beta program.) The ``letsencrypt`` commandline tool has a builtin help: @@ -74,8 +74,8 @@ Running with Docker =================== Docker_ is another way to quickly obtain testing certs. From the -server that the domain your requesting a cert for resolves to, -`install Docker`_, issue the following command: +server that the domain you're requesting a cert for resolves to, +`install Docker`_, then issue the following command: .. code-block:: shell From 2c66c7b3339ab1ccb326000fed63698fc5b2ec92 Mon Sep 17 00:00:00 2001 From: Dev & Sec Date: Mon, 26 Oct 2015 23:22:22 +0000 Subject: [PATCH 133/216] use more accurate method to calculate time interval in should_autodeploy() and should_autorenew() functions --- letsencrypt/storage.py | 29 +++++++++-------- letsencrypt/tests/renewer_test.py | 52 ++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 26 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index ece3df4b7..52be94f68 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -2,7 +2,6 @@ import datetime import os import re -import time import configobj import parsedatetime @@ -24,8 +23,8 @@ def config_with_defaults(config=None): return defaults_copy -def parse_time_interval(interval, textparser=parsedatetime.Calendar()): - """Parse the time specified time interval. +def add_time_interval(base_time, interval, textparser=parsedatetime.Calendar()): + """Parse the time specified time interval, and add it to the base_time The interval can be in the English-language format understood by parsedatetime, e.g., '10 days', '3 weeks', '6 months', '9 hours', or @@ -33,15 +32,19 @@ def parse_time_interval(interval, textparser=parsedatetime.Calendar()): hours'. If an integer is found with no associated unit, it is interpreted by default as a number of days. + :param datetime.datetime base_time: The time to be added with the interval. :param str interval: The time interval to parse. - :returns: The interpretation of the time interval. - :rtype: :class:`datetime.timedelta`""" + :returns: The base_time plus the interpretation of the time interval. + :rtype: :class:`datetime.datetime`""" if interval.strip().isdigit(): interval += " days" - return datetime.timedelta(0, time.mktime(textparser.parse( - interval, time.localtime(0))[0])) + + # try to use the same timezone, but fallback to UTC + tzinfo = base_time.tzinfo or pytz.UTC + + return textparser.parseDT(interval, base_time, tzinfo=tzinfo)[0] class RenewableCert(object): # pylint: disable=too-many-instance-attributes @@ -465,11 +468,9 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes if self.has_pending_deployment(): interval = self.configuration.get("deploy_before_expiry", "5 days") - autodeploy_interval = parse_time_interval(interval) expiry = crypto_util.notAfter(self.current_target("cert")) - now = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC) - remaining = expiry - now - if remaining < autodeploy_interval: + now = pytz.UTC.fromutc(datetime.datetime.utcnow()) + if expiry < add_time_interval(now, interval): return True return False @@ -532,12 +533,10 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # Renewals on the basis of expiry time interval = self.configuration.get("renew_before_expiry", "10 days") - autorenew_interval = parse_time_interval(interval) expiry = crypto_util.notAfter(self.version( "cert", self.latest_common_version())) - now = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC) - remaining = expiry - now - if remaining < autorenew_interval: + now = pytz.UTC.fromutc(datetime.datetime.utcnow()) + if expiry < add_time_interval(now, interval): return True return False diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index a856fcf4f..2123db367 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -1,5 +1,6 @@ """Tests for letsencrypt.renewer.""" import datetime +import pytz import os import tempfile import shutil @@ -622,18 +623,47 @@ class RenewableCertTests(BaseRenewableCertTest): # OCSP server to test against. self.assertFalse(self.test_rc.ocsp_revoked()) - def test_parse_time_interval(self): + def test_add_time_interval(self): from letsencrypt import storage - # XXX: I'm not sure if intervals related to years and months - # take account of the current date (if so, some of these - # may fail in the future, like in leap years or even in - # months of different lengths!) - intended = {"": 0, "17 days": 17, "23": 23, "1 month": 31, - "7 weeks": 49, "1 year 1 day": 366, "1 year-1 day": 364, - "4 years": 1461} - for time in intended: - self.assertEqual(storage.parse_time_interval(time), - datetime.timedelta(intended[time])) + + # this month has 30 days, and the next year is a leap year + time_1 = pytz.UTC.fromutc(datetime.datetime(2003, 11, 20, 11, 59, 21)) + + # this month has 31 days, and the next year is not a leap year + time_2 = pytz.UTC.fromutc(datetime.datetime(2012, 10, 18, 21, 31, 16)) + + # in different time zone (GMT+8) + time_3 = pytz.timezone('Asia/Shanghai').fromutc( + datetime.datetime(2015, 10, 26, 22, 25, 41)) + + intended = { + (time_1, ""): time_1, + (time_2, ""): time_2, + (time_3, ""): time_3, + (time_1, "17 days"): time_1 + datetime.timedelta(17), + (time_2, "17 days"): time_2 + datetime.timedelta(17), + (time_1, "30"): time_1 + datetime.timedelta(30), + (time_2, "30"): time_2 + datetime.timedelta(30), + (time_1, "7 weeks"): time_1 + datetime.timedelta(49), + (time_2, "7 weeks"): time_2 + datetime.timedelta(49), + # 1 month is always 30 days, no matter which month it is + (time_1, "1 month"): time_1 + datetime.timedelta(30), + (time_2, "1 month"): time_2 + datetime.timedelta(31), + # 1 year could be 365 or 366 days, depends on the year + (time_1, "1 year"): time_1 + datetime.timedelta(366), + (time_2, "1 year"): time_2 + datetime.timedelta(365), + (time_1, "1 year 1 day"): time_1 + datetime.timedelta(367), + (time_2, "1 year 1 day"): time_2 + datetime.timedelta(366), + (time_1, "1 year-1 day"): time_1 + datetime.timedelta(365), + (time_2, "1 year-1 day"): time_2 + datetime.timedelta(364), + (time_1, "4 years"): time_1 + datetime.timedelta(1461), + (time_2, "4 years"): time_2 + datetime.timedelta(1461), + } + + for parameters, excepted in intended.items(): + base_time, interval = parameters + self.assertEqual(storage.add_time_interval(base_time, interval), + excepted) @mock.patch("letsencrypt.renewer.plugins_disco") @mock.patch("letsencrypt.account.AccountFileStorage") From 1d13938dbfe4c3712e64936ec0ea9192e2cfd19a Mon Sep 17 00:00:00 2001 From: Scott Merrill Date: Mon, 26 Oct 2015 19:50:37 -0400 Subject: [PATCH 134/216] Correct typo and whitespace issues * s/privilidged/privileged/ * s/a HTTP/an HTTP/ * Add whitespace at the end of the lines to improve user experience The lack of trailing whitespace on these entries causes Debian's debconf interface to join the last word of a line with the first word of the next line, with no space in between. --- letsencrypt/plugins/manual.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 61a5acdb3..60b816821 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -90,10 +90,10 @@ s.serve_forever()" """ def more_info(self): # pylint: disable=missing-docstring,no-self-use return """\ -This plugin requires user's manual intervention in setting up a HTTP -server for solving SimpleHTTP challenges and thus does not need to be -run as a privilidged process. Alternatively shows instructions on how -to use Python's built-in HTTP server and, in case of HTTPS, openssl +This plugin requires user's manual intervention in setting up an HTTP +server for solving SimpleHTTP challenges and thus does not need to be +run as a privileged process. Alternatively shows instructions on how +to use Python's built-in HTTP server and, in case of HTTPS, openssl binary for temporary key/certificate generation.""".replace("\n", "") def get_chall_pref(self, domain): From f2cb43762d448b293c60c65a061c78c692f65476 Mon Sep 17 00:00:00 2001 From: venyii Date: Tue, 27 Oct 2015 18:38:28 +0100 Subject: [PATCH 135/216] Remove duplicated 'that' in the Plugins section --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 7579a3c4b..4f488e2a2 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -143,7 +143,7 @@ nginx Y Y Very experimental. Not included in letsencrypt-auto_. ========== = = ================================================================ Third party plugins are listed at -https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If that +https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If that's not enough, you can always :ref:`write your own plugin `. From 504ae5e867889ea43193f10f0d9ca6f32777ad38 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 27 Oct 2015 10:40:00 -0700 Subject: [PATCH 136/216] Added pkg-config --- bootstrap/_arch_common.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap/_arch_common.sh b/bootstrap/_arch_common.sh index da78ead01..9e0d96e33 100755 --- a/bootstrap/_arch_common.sh +++ b/bootstrap/_arch_common.sh @@ -17,3 +17,4 @@ pacman -S --needed \ openssl \ libffi \ ca-certificates \ + pkg-config \ From b53691860fbefab26361288eb05b37a20c245f16 Mon Sep 17 00:00:00 2001 From: Till Maas Date: Tue, 27 Oct 2015 20:04:20 +0100 Subject: [PATCH 137/216] Actually use BITS constant in example client --- examples/acme_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/acme_client.py b/examples/acme_client.py index eacfb8ab8..f9408c2af 100644 --- a/examples/acme_client.py +++ b/examples/acme_client.py @@ -22,7 +22,7 @@ DOMAIN = 'example1.com' # example.com is ignored by Boulder # generate_private_key requires cryptography>=0.5 key = jose.JWKRSA(key=rsa.generate_private_key( public_exponent=65537, - key_size=2048, + key_size=BITS, backend=default_backend())) acme = client.Client(NEW_REG_URL, key) From f1f8a493a0c920d775a0311d94e9a3d98911d27f Mon Sep 17 00:00:00 2001 From: Till Maas Date: Tue, 27 Oct 2015 20:09:59 +0100 Subject: [PATCH 138/216] Directly link to contributing docs in README --- README.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 769bc7a8f..d3e89c939 100644 --- a/README.rst +++ b/README.rst @@ -110,7 +110,7 @@ Documentation: https://letsencrypt.readthedocs.org Software project: https://github.com/letsencrypt/letsencrypt -Notes for developers: CONTRIBUTING.md_ +Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html Main Website: https://letsencrypt.org/ @@ -123,4 +123,3 @@ email to client-dev+subscribe@letsencrypt.org) .. _Freenode: https://freenode.net .. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev -.. _CONTRIBUTING.md: https://github.com/letsencrypt/letsencrypt/blob/master/CONTRIBUTING.md From e5f06bacbd5d7d4e050ecfc7d844d2fe1941c2dc Mon Sep 17 00:00:00 2001 From: Till Maas Date: Tue, 27 Oct 2015 20:10:47 +0100 Subject: [PATCH 139/216] Add missing newline to standalone README --- acme/examples/standalone/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme/examples/standalone/README b/acme/examples/standalone/README index 89bc5d74e..8bb0fad97 100644 --- a/acme/examples/standalone/README +++ b/acme/examples/standalone/README @@ -1,2 +1,2 @@ python -m acme.standalone -p 1234 -curl -k https://localhost:1234 \ No newline at end of file +curl -k https://localhost:1234 From e9a2180d16bf8efef4795ccd25ef81f58f2f184f Mon Sep 17 00:00:00 2001 From: Thom Wiggers Date: Tue, 27 Oct 2015 22:53:20 +0100 Subject: [PATCH 140/216] Add gitattributes file to mark bat file as CRLF Gitattributes files can be used to mark files as crlf, which is useful for the `.bat` files in case people use eol=lf in their git config. --- .gitattributes | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..5eee84cce --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* text=auto eol=lf + +# special files +*.bat text eol=crlf +*.jpeg binary +*.jpg binary +*.png binary From 5d11691de77d117b58f722857d79a04e2f1e278d Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 27 Oct 2015 22:19:21 +0000 Subject: [PATCH 141/216] Standalone verifies ports for suported challenges only (fixes #1149). --- letsencrypt/plugins/standalone.py | 12 ++++++++++-- letsencrypt/plugins/standalone_test.py | 13 +++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py index d4dddc7f6..5d2dafc7d 100644 --- a/letsencrypt/plugins/standalone.py +++ b/letsencrypt/plugins/standalone.py @@ -181,6 +181,15 @@ class Authenticator(common.Plugin): return set(challenges.Challenge.TYPES[name] for name in self.conf("supported-challenges").split(",")) + @property + def _necessary_ports(self): + necessary_ports = set() + if challenges.SimpleHTTP in self.supported_challenges: + necessary_ports.add(self.config.simple_http_port) + if challenges.DVSNI in self.supported_challenges: + necessary_ports.add(self.config.dvsni_port) + return necessary_ports + def more_info(self): # pylint: disable=missing-docstring return self.__doc__ @@ -194,8 +203,7 @@ class Authenticator(common.Plugin): return chall_pref def perform(self, achalls): # pylint: disable=missing-docstring - if any(util.already_listening(port) for port in - (self.config.dvsni_port, self.config.simple_http_port)): + if any(util.already_listening(port) for port in self._necessary_ports): raise errors.MisconfigurationError( "At least one of the (possibly) required ports is " "already taken.") diff --git a/letsencrypt/plugins/standalone_test.py b/letsencrypt/plugins/standalone_test.py index 0ccdccb1f..3a6941be8 100644 --- a/letsencrypt/plugins/standalone_test.py +++ b/letsencrypt/plugins/standalone_test.py @@ -107,10 +107,15 @@ class AuthenticatorTest(unittest.TestCase): set([challenges.DVSNI, challenges.SimpleHTTP])) @mock.patch("letsencrypt.plugins.standalone.util") - def test_perform_misconfiguration(self, mock_util): - mock_util.already_listening.return_value = True - self.assertRaises(errors.MisconfigurationError, self.auth.perform, []) - mock_util.already_listening.assert_called_once_with(1234) + def test_perform_alredy_listening(self, mock_util): + for chall, port in ((challenges.DVSNI.typ, 1234), + (challenges.SimpleHTTP.typ, 4321)): + mock_util.already_listening.return_value = True + self.config.standalone_supported_challenges = chall + self.assertRaises( + errors.MisconfigurationError, self.auth.perform, []) + mock_util.already_listening.assert_called_once_with(port) + mock_util.already_listening.reset_mock() @mock.patch("letsencrypt.plugins.standalone.zope.component.getUtility") def test_perform(self, unused_mock_get_utility): From 8c7d0614e5b13f1ca487a8bd1ab0568fcf3f0437 Mon Sep 17 00:00:00 2001 From: Chhatoi Pritam Baral Date: Wed, 28 Oct 2015 03:40:08 +0530 Subject: [PATCH 142/216] Respect pre-installed gcc package on Arch Linux --- bootstrap/_arch_common.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bootstrap/_arch_common.sh b/bootstrap/_arch_common.sh index 9e0d96e33..6895addf4 100755 --- a/bootstrap/_arch_common.sh +++ b/bootstrap/_arch_common.sh @@ -4,6 +4,15 @@ # - Manjaro 15.09 (x86_64) # - ArchLinux (x86_64) +# Both "gcc-multilib" and "gcc" packages provide gcc. If user already has +# "gcc-multilib" installed, let's stick to their choice +if pacman -Qc gcc-multilib &>/dev/null +then + GCC_PACKAGE="gcc-multilib"; +else + GCC_PACKAGE="gcc"; +fi + # "python-virtualenv" is Python3, but "python2-virtualenv" provides # only "virtualenv2" binary, not "virtualenv" necessary in # ./bootstrap/dev/_common_venv.sh @@ -11,7 +20,7 @@ pacman -S --needed \ git \ python2 \ python-virtualenv \ - gcc \ + "$GCC_PACKAGE" \ dialog \ augeas \ openssl \ From 7b5f20a31ca0e9e7031b827fa3a003bf5a0b4c02 Mon Sep 17 00:00:00 2001 From: Chhatoi Pritam Baral Date: Wed, 28 Oct 2015 04:03:44 +0530 Subject: [PATCH 143/216] Make message printed after bootstrap slightly less confusing --- bootstrap/dev/_venv_common.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/bootstrap/dev/_venv_common.sh b/bootstrap/dev/_venv_common.sh index 2d84dc39b..d07f38ed8 100755 --- a/bootstrap/dev/_venv_common.sh +++ b/bootstrap/dev/_venv_common.sh @@ -21,5 +21,6 @@ pip install -U setuptools pip install -U pip pip install "$@" +set +x echo "Please run the following command to activate developer environment:" echo "source $VENV_NAME/bin/activate" From 3ecf38b401eaf1ff1efc174172876b2e2e0b9748 Mon Sep 17 00:00:00 2001 From: Christopher Manning Date: Tue, 27 Oct 2015 17:28:04 -0500 Subject: [PATCH 144/216] Vagrantfile: use recommended bootstrap scripts for provisioning Also remove an incorrect command from the Vagrant instructions in `docs/contributing.rst`. --- Vagrantfile | 9 +++------ docs/contributing.rst | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index d54e4ea23..a2759440c 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -4,14 +4,11 @@ # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" -# Setup instructions from docs/using.rst +# Setup instructions from docs/contributing.rst $ubuntu_setup_script = < Date: Tue, 27 Oct 2015 22:35:17 -0700 Subject: [PATCH 145/216] Install listenbuddy --- tests/boulder-start.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/boulder-start.sh b/tests/boulder-start.sh index 051c832f2..47c1b6278 100755 --- a/tests/boulder-start.sh +++ b/tests/boulder-start.sh @@ -37,5 +37,7 @@ wget https://github.com/jsha/boulder-tools/raw/master/goose.gz && \ zcat goose.gz > $GOPATH/bin/goose && \ chmod +x $GOPATH/bin/goose ./test/create_db.sh +# listenbuddy is needed for ./start.py +go get github.com/jsha/listenbuddy ./start.py & # Hopefully start.py bootstraps before integration test is started... From e8cfedb34d853b6c86a83ba12a3884eaf28a5451 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 07:10:13 +0000 Subject: [PATCH 146/216] Move example ACME client to acme subpkg --- examples/acme_client.py => acme/examples/example_client.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/acme_client.py => acme/examples/example_client.py (100%) diff --git a/examples/acme_client.py b/acme/examples/example_client.py similarity index 100% rename from examples/acme_client.py rename to acme/examples/example_client.py From f42515ebe4df620921db592e3cde9ddb75a7005b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 07:16:40 +0000 Subject: [PATCH 147/216] Include example ACME client in docs --- acme/docs/index.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/acme/docs/index.rst b/acme/docs/index.rst index 8d298054e..a200808da 100644 --- a/acme/docs/index.rst +++ b/acme/docs/index.rst @@ -17,6 +17,12 @@ Contents: :members: +Example client: + +.. include:: ../examples/example_client.py + :code: python + + Indices and tables ================== From 323f9a10a1e20e616cbf22e5ccf2d5e5516b36cf Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 07:26:52 +0000 Subject: [PATCH 148/216] Update example ACME client to work with Boulder --- acme/acme/testdata/README | 6 +++--- acme/acme/testdata/cert.der | Bin 377 -> 771 bytes acme/acme/testdata/csr.der | Bin 210 -> 607 bytes acme/acme/testdata/rsa2048_key.pem | 27 +++++++++++++++++++++++++++ acme/examples/example_client.py | 4 ++-- 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 acme/acme/testdata/rsa2048_key.pem diff --git a/acme/acme/testdata/README b/acme/acme/testdata/README index 11bca55e5..dfe3f5405 100644 --- a/acme/acme/testdata/README +++ b/acme/acme/testdata/README @@ -4,12 +4,12 @@ to use appropriate extension for vector filenames: .pem for PEM and The following command has been used to generate test keys: - for x in 256 512 1024; do openssl genrsa -out rsa${k}_key.pem $k; done + for x in 256 512 1024 2048; do openssl genrsa -out rsa${k}_key.pem $k; done and for the CSR: - openssl req -key rsa512_key.pem -new -subj '/CN=example.com' -outform DER > csr.der + openssl req -key rsa2048_key.pem -new -subj '/CN=example.com' -outform DER > csr.der and for the certificate: - openssl req -key rsa512_key.pem -new -subj '/CN=example.com' -x509 -outform DER > cert.der + openssl req -key rsa2047_key.pem -new -subj '/CN=example.com' -x509 -outform DER > cert.der diff --git a/acme/acme/testdata/cert.der b/acme/acme/testdata/cert.der index 5f1018505d81a50ed3239d829533deac5fcc2085..ab231982f25ba5b804bc3e4249ec71f8a98109ab 100644 GIT binary patch delta 676 zcmV;V0$ct00)qw^FoFXAFoFT+paTK{0s;vD!GbbZT8w0>kr*u*F)%VXFgG$ZG%;Ei z4KXz_F)}wWH!?IdF_B&xe}VxbFbxI?Duzgg_YDC70R;d9f&mWzFoFRJ0)hbn0PuVn zFn5TUAL295=kO%LM}nSV0Cs?}j^6(Oi-f*9qo2j6=x!V{K8;&aU<&F-RI29O8% zld7Z4fcu;4&+FkVruZ1kp7d(v3v-9~!hGOJB9JhHd1LNh4#-X9kUhXI+oHN?r!`88 z97z;ty1!m4{8!1ZBA&QL?j6why;pO+J3mKr<%2n26wHQY1l7i#{!}z6vumf3&=fWF zHVyL02Ouf)^_JEvWuvqeyjV9d9|i+e9U}x7FcyFm^fnFh$p;`Q^Yxb2D`lg!6}(tC zlNkXXe}Vx40PR%mGTHF`qfZ|f`W9WB?Zir{Hw4`a_UhE50Ig1562Q&I3%9!Yc`^h9>(8O2@#PSQ6nHZUvI2qO+nwIZ<+c0LLgs!B4nURr!iGhK!d6YP>p{ap6 zj5{$-sy@bmmyJ`a&7@EAwlr%Zv3bCH`*@J!N8MWMEtz zVBlvU%f=ik%f}+dBBFNw#9Qy{Q#L(OWsXqkXE{)Eb-GfQfjmfFnMJ}ttO1*fuE`G> z<(wQDc)I@!HO%vwut#mfG=7{>z}O6 Yi?Kd^cJYN9>LqE5%h;CwZnWA40D^aK-~a#s diff --git a/acme/acme/testdata/csr.der b/acme/acme/testdata/csr.der index adc29ff18463752b4b9ab26a0dd77d2621363725..d43ac85a16a5ec7cb3db7895f0c67f9e0a52f492 100644 GIT binary patch literal 607 zcmV-l0-*gcf&yDGf&oJU0RS)-F%&Qo1_M&L zNQUJrzh?pPZGtcMnB*I66o?!rXfU%C=|1mCW zK0RUgWxQN(A@qET;Gv%bxUV&LNQU#G~q&8=D4keLkuj9-(Pt^OR@O_IPL=QHku|DZK zfPJiAQB4p_Swh3a05oAZ^u*Ninui6~Hq0^p1q5)wAV5^0MOfjL!Rrv=Ri3Y<#X`fc zl^_!WP#SW@J_cxpHkAXbCT@t28BvIGy*v3BV8s=BjO0B=Nwm)83Y7o= delta 192 zcmcc5a*2`Epz*vx^+XPt`WORVHcqWJkGAi;jEsz|49wmP1|Cd~3~Ne@w* Date: Wed, 28 Oct 2015 08:20:58 +0000 Subject: [PATCH 149/216] No newlines in JWK thumbprint (fixes #1165) --- acme/acme/jose/jwk.py | 2 +- acme/acme/jose/jwk_test.py | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/acme/acme/jose/jwk.py b/acme/acme/jose/jwk.py index 74fa72319..da61b0c4e 100644 --- a/acme/acme/jose/jwk.py +++ b/acme/acme/jose/jwk.py @@ -35,7 +35,7 @@ class JWK(json_util.TypedJSONObjectWithFields): _thumbprint_json_dumps_params = { # "no whitespace or line breaks before or after any syntactic # elements" - 'indent': 0, + 'indent': None, 'separators': (',', ':'), # "members ordered lexicographically by the Unicode [UNICODE] # code points of the member names" diff --git a/acme/acme/jose/jwk_test.py b/acme/acme/jose/jwk_test.py index d8a7410e8..eea5793bf 100644 --- a/acme/acme/jose/jwk_test.py +++ b/acme/acme/jose/jwk_test.py @@ -1,4 +1,5 @@ """Tests for acme.jose.jwk.""" +import binascii import unittest from acme import test_util @@ -40,8 +41,9 @@ class JWKTestBaseMixin(object): class JWKOctTest(unittest.TestCase, JWKTestBaseMixin): """Tests for acme.jose.jwk.JWKOct.""" - thumbprint = (b"=,\xdd;I\x1a+i\x02x\x8a\x12?06IM\xc2\x80" - b"\xe4\xc3\x1a\xfc\x89\xf3)'\xce\xccm\xfd5") + thumbprint = (b"\xf3\xe7\xbe\xa8`\xd2\xdap\xe9}\x9c\xce>" + b"\xd0\xfcI\xbe\xcd\x92'\xd4o\x0e\xf41\xea" + b"\x8e(\x8a\xb2i\x1c") def setUp(self): from acme.jose.jwk import JWKOct @@ -71,8 +73,8 @@ class JWKRSATest(unittest.TestCase, JWKTestBaseMixin): """Tests for acme.jose.jwk.JWKRSA.""" # pylint: disable=too-many-instance-attributes - thumbprint = (b'\x08\xfa1\x87\x1d\x9b6H/*\x1eW\xc2\xe3\xf6P' - b'\xefs\x0cKB\x87\xcf\x85yO\x045\x0e\x91\x80\x0b') + thumbprint = (b'\x83K\xdc#3\x98\xca\x98\xed\xcb\x80\x80<\x0c' + b'\xf0\x95\xb9H\xb2*l\xbd$\xe5&|O\x91\xd4 \xb0Y') def setUp(self): from acme.jose.jwk import JWKRSA @@ -168,6 +170,22 @@ class JWKRSATest(unittest.TestCase, JWKTestBaseMixin): self.assertRaises(errors.DeserializationError, JWK.from_json, {'kty': 'RSA', 'e': 'AQAB', 'n': '1'}) + def test_thumbprint_go_jose(self): + # https://github.com/square/go-jose/blob/4ddd71883fa547d37fbf598071f04512d8bafee3/jwk.go#L155 + # https://github.com/square/go-jose/blob/4ddd71883fa547d37fbf598071f04512d8bafee3/jwk_test.go#L331-L344 + # https://github.com/square/go-jose/blob/4ddd71883fa547d37fbf598071f04512d8bafee3/jwk_test.go#L384 + from acme.jose.jwk import JWKRSA + key = JWKRSA.json_loads("""{ + "kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB" +}""") + self.assertEqual( + binascii.hexlify(key.thumbprint()), + b"f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932") + if __name__ == '__main__': unittest.main() # pragma: no cover From f8da08e6a282f338791c219af5a5e0f2159c7e4d Mon Sep 17 00:00:00 2001 From: Christoph Kisfeld Date: Wed, 28 Oct 2015 11:35:39 +0100 Subject: [PATCH 150/216] Fix readthedocs Intersphinx URLs, fix #1140 --- docs/conf.py | 2 +- letsencrypt-apache/docs/conf.py | 4 ++-- letsencrypt-compatibility-test/docs/conf.py | 4 ++-- letsencrypt-nginx/docs/conf.py | 4 ++-- letshelp-letsencrypt/docs/conf.py | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 124f0f9ad..62a7cea07 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -315,5 +315,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), - 'acme': ('https://acme-python.readthedocs.org', None), + 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-apache/docs/conf.py b/letsencrypt-apache/docs/conf.py index e439428af..ddbf09262 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/letsencrypt-apache/docs/conf.py @@ -313,6 +313,6 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), - 'acme': ('https://acme-python.readthedocs.org', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), + 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index 5a63c1dca..cd7a970a3 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -307,8 +307,8 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), - 'acme': ('https://acme-python.readthedocs.org', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), + 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), 'letsencrypt-apache': ('https://letsencrypt-apache.readthedocs.org', None), 'letsencrypt-nginx': ('https://letsencrypt-nginx.readthedocs.org', None), } diff --git a/letsencrypt-nginx/docs/conf.py b/letsencrypt-nginx/docs/conf.py index 8bcae3a78..cdb3490a0 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/letsencrypt-nginx/docs/conf.py @@ -306,6 +306,6 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), - 'acme': ('https://acme-python.readthedocs.org', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), + 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-letsencrypt/docs/conf.py index abbf3621d..206b0b9e2 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-letsencrypt/docs/conf.py @@ -306,6 +306,6 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), - 'acme': ('https://acme-python.readthedocs.org', None), - 'letsencrypt': ('https://letsencrypt.readthedocs.org', None), + 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), + 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), } From eb41678bcd65e5ca4e353d4c45a7ff914d3a9ea8 Mon Sep 17 00:00:00 2001 From: Christoph Kisfeld Date: Wed, 28 Oct 2015 11:49:32 +0100 Subject: [PATCH 151/216] Fix one more readthedocs Intersphinx URL --- letsencrypt-compatibility-test/docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-compatibility-test/docs/conf.py b/letsencrypt-compatibility-test/docs/conf.py index cd7a970a3..7e9f0d5a4 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/letsencrypt-compatibility-test/docs/conf.py @@ -309,6 +309,6 @@ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), 'letsencrypt': ('https://letsencrypt.readthedocs.org/en/latest/', None), - 'letsencrypt-apache': ('https://letsencrypt-apache.readthedocs.org', None), - 'letsencrypt-nginx': ('https://letsencrypt-nginx.readthedocs.org', None), + 'letsencrypt-apache': ('https://letsencrypt-apache.readthedocs.org/en/latest/', None), + 'letsencrypt-nginx': ('https://letsencrypt-nginx.readthedocs.org/en/latest/', None), } From df2ba1ba46e550cc2b7fac54b8e259755aca9186 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 28 Oct 2015 19:13:41 -0700 Subject: [PATCH 152/216] standalone_description += pde_feedback --- letsencrypt/plugins/standalone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py index 459988008..1b56fbdbc 100644 --- a/letsencrypt/plugins/standalone.py +++ b/letsencrypt/plugins/standalone.py @@ -144,7 +144,7 @@ class Authenticator(common.Plugin): zope.interface.implements(interfaces.IAuthenticator) zope.interface.classProvides(interfaces.IPluginFactory) - description = "Automatically configure and run a simple webserver" + description = "Automatically use a temporary webserver" def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) From c93512d2e18359667754f09813bc34c339df8e8d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 25 Oct 2015 18:47:52 -0700 Subject: [PATCH 153/216] Make "certonly" a synonym for "auth", and document it Closes: #657 --- letsencrypt/cli.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 94bda18ea..e88302c81 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -56,7 +56,7 @@ default, it will attempt to use a webserver both for obtaining and installing the cert. Major SUBCOMMANDS are: (default) run Obtain & install a cert in your current webserver - auth Authenticate & obtain cert, but do not install it + certonly Aka "auth": obtain cert, but do not install it install Install a previously obtained cert in a server revoke Revoke a previously obtained certificate rollback Rollback server configuration changes made during install @@ -358,11 +358,11 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): if os.path.exists("/etc/debian_version"): # Debian... installers are at least possible msg = ('No installers seem to be present and working on your system; ' - 'fix that or try running letsencrypt with the "auth" command') + 'fix that or try running letsencrypt with the "certonly" command') else: # XXX update this logic as we make progress on #788 and nginx support msg = ('No installers are available on your OS yet; try running ' - '"letsencrypt-auto auth" to get a cert you can install manually') + '"letsencrypt-auto certonly" to get a cert you can install manually') else: msg = "{0} could not be determined or is not installed".format(cfg_type) raise PluginSelectionError(msg) @@ -377,7 +377,7 @@ def choose_configurator_plugins(args, config, plugins, verb): # Which plugins do we need? need_inst = need_auth = (verb == "run") - if verb == "auth": + if verb in ("auth","certonly"): need_auth = True if verb == "install": need_inst = True @@ -457,7 +457,7 @@ def auth(args, config, plugins): try: # installers are used in auth mode to determine domain names - installer, authenticator = choose_configurator_plugins(args, config, plugins, "auth") + installer, authenticator = choose_configurator_plugins(args, config, plugins, "certonly") except PluginSelectionError, e: return e.message @@ -481,7 +481,7 @@ def install(args, config, plugins): # XXX: Update for renewer/RenewableCert try: - installer, _ = choose_configurator_plugins(args, config, plugins, "auth") + installer, _ = choose_configurator_plugins(args, config, plugins, "certonly") except PluginSelectionError, e: return e.message @@ -609,7 +609,7 @@ class HelpfulArgumentParser(object): """ # Maps verbs/subcommands to the functions that implement them - VERBS = {"auth": auth, "config_changes": config_changes, + VERBS = {"auth": auth, "certonly": auth, "config_changes": config_changes, "install": install, "plugins": plugins_cmd, "revoke": revoke, "rollback": rollback, "run": run} @@ -894,7 +894,7 @@ def _paths_parser(helpful): "paths", description="Arguments changing execution paths & servers") cph = "Path to where cert is saved (with auth), installed (with install --csr) or revoked." - if verb == "auth": + if verb in ("auth", "certonly"): add("paths", "--cert-path", default=flag_default("auth_cert_path"), help=cph) elif verb == "revoke": add("paths", "--cert-path", type=read_file, required=True, help=cph) @@ -907,7 +907,7 @@ def _paths_parser(helpful): help="Path to private key for cert creation or revocation (if account key is missing)") default_cp = None - if verb == "auth": + if verb in ("auth", "certonly"): default_cp = flag_default("auth_chain_path") add("paths", "--fullchain-path", default=default_cp, help="Accompanying path to a full certificate chain (cert plus chain).") From 585e6cee6c685033bcb7488589e328bab7703933 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 10:56:32 -0700 Subject: [PATCH 154/216] Make "everything" a synonym for "run" --- letsencrypt/cli.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index e88302c81..700c752c2 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -377,7 +377,7 @@ def choose_configurator_plugins(args, config, plugins, verb): # Which plugins do we need? need_inst = need_auth = (verb == "run") - if verb in ("auth","certonly"): + if verb in ("auth", "certonly"): need_auth = True if verb == "install": need_inst = True @@ -669,7 +669,12 @@ class HelpfulArgumentParser(object): for i, token in enumerate(self.args): if token in self.VERBS: - self.verb = token + verb = token + if verb == "certonly": + verb = "auth" + if verb == "everything": + verb = "run" + self.verb = verb self.args.pop(i) return From ec25612d6026610e5f8be5e59bd838d8bd453394 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 10:57:46 -0700 Subject: [PATCH 155/216] For now, use the absolutely minimal implementation of verb synonyms Under the hood, everything can remain "run" and "auth" for now. --- letsencrypt/cli.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 700c752c2..b7af4bf9f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -377,7 +377,7 @@ def choose_configurator_plugins(args, config, plugins, verb): # Which plugins do we need? need_inst = need_auth = (verb == "run") - if verb in ("auth", "certonly"): + if verb == "auth": need_auth = True if verb == "install": need_inst = True @@ -457,7 +457,7 @@ def auth(args, config, plugins): try: # installers are used in auth mode to determine domain names - installer, authenticator = choose_configurator_plugins(args, config, plugins, "certonly") + installer, authenticator = choose_configurator_plugins(args, config, plugins, "auth") except PluginSelectionError, e: return e.message @@ -481,7 +481,7 @@ def install(args, config, plugins): # XXX: Update for renewer/RenewableCert try: - installer, _ = choose_configurator_plugins(args, config, plugins, "certonly") + installer, _ = choose_configurator_plugins(args, config, plugins, "auth") except PluginSelectionError, e: return e.message @@ -899,7 +899,7 @@ def _paths_parser(helpful): "paths", description="Arguments changing execution paths & servers") cph = "Path to where cert is saved (with auth), installed (with install --csr) or revoked." - if verb in ("auth", "certonly"): + if verb == "auth": add("paths", "--cert-path", default=flag_default("auth_cert_path"), help=cph) elif verb == "revoke": add("paths", "--cert-path", type=read_file, required=True, help=cph) @@ -912,7 +912,7 @@ def _paths_parser(helpful): help="Path to private key for cert creation or revocation (if account key is missing)") default_cp = None - if verb in ("auth", "certonly"): + if verb == "auth": default_cp = flag_default("auth_chain_path") add("paths", "--fullchain-path", default=default_cp, help="Accompanying path to a full certificate chain (cert plus chain).") From 84dad86d6197ba5de2b2c9dd96f6b3eae2cb9712 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 11:27:31 -0700 Subject: [PATCH 156/216] Update the help for modern verbiage --- letsencrypt/cli.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b7af4bf9f..a49a33f26 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -56,7 +56,7 @@ default, it will attempt to use a webserver both for obtaining and installing the cert. Major SUBCOMMANDS are: (default) run Obtain & install a cert in your current webserver - certonly Aka "auth": obtain cert, but do not install it + certonly Obtain cert, but do not install it (aka "auth") install Install a previously obtained cert in a server revoke Revoke a previously obtained certificate rollback Rollback server configuration changes made during install @@ -67,12 +67,14 @@ the cert. Major SUBCOMMANDS are: # This is the short help for letsencrypt --help, where we disable argparse # altogether -USAGE = SHORT_USAGE + """Choice of server for authentication/installation: +USAGE = SHORT_USAGE + """Choice of server plugins for obtaining and installing cert: --apache Use the Apache plugin for authentication & installation --nginx Use the Nginx plugin for authentication & installation --standalone Run a standalone webserver for authentication - OR: + +OR use different servers to obtain (authenticate) the cert and then install it: + --authenticator standalone --installer nginx More detailed help: @@ -80,8 +82,8 @@ More detailed help: -h, --help [topic] print this message, or detailed help on a topic; the available topics are: - all, apache, automation, manual, nginx, paths, security, testing, or any of - the subcommands + all, automation, paths, security, testing, or any of the subcommands or + plugins (certonly, install, nginx, apache, standalone, etc) """ @@ -759,6 +761,10 @@ class HelpfulArgumentParser(object): """ # topics maps each topic to whether it should be documented by # argparse on the command line + if chosen_topic == "certonly": + chosen_topic = "auth" + if chosen_topic == "everything": + chosen_topic = "run" if chosen_topic == "all": return dict([(t, True) for t in self.help_topics]) elif not chosen_topic: From 9a6ecbe669fd250b055cbab062f2540734b5b28b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 11:53:41 -0700 Subject: [PATCH 157/216] Test case for new verb variant --- letsencrypt/tests/cli_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index cb2360dd0..27892cdc4 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -115,6 +115,12 @@ class CLITest(unittest.TestCase): # (we can only do that if letsencrypt-nginx is actually present) ret, _, _, _ = self._call(args) self.assertTrue("The nginx plugin is not working" in ret) + self.assertTrue("Could not find configuration root" in ret) + + with MockedVerb("auth") as mock_auth: + from letsencrypt import cli + self._call(["certonly", "--standalone"]) + self.assertEqual(1, mock_auth.call_count) def test_rollback(self): _, _, _, client = self._call(['rollback']) From 00dcff6de9ed41d411a5e37f42b1d723f887ec77 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 11:54:47 -0700 Subject: [PATCH 158/216] Also slip in some extra conditional plugin cli unit tests --- letsencrypt/tests/cli_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 27892cdc4..d8940e22c 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -116,6 +116,7 @@ class CLITest(unittest.TestCase): ret, _, _, _ = self._call(args) self.assertTrue("The nginx plugin is not working" in ret) self.assertTrue("Could not find configuration root" in ret) + self.assertTrue("NoInstallationError" in ret) with MockedVerb("auth") as mock_auth: from letsencrypt import cli From ed69ae0292a016ce9bf5038c5d32ed0977f9b1f6 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 12:06:05 -0700 Subject: [PATCH 159/216] Don't suggest that --nginx is available if it isn't Fixes: #932 --- letsencrypt/cli.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index a49a33f26..9fa765a2e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -64,18 +64,24 @@ the cert. Major SUBCOMMANDS are: """ +plugins = plugins_disco.PluginsRegistry.find_all() +if "nginx" in plugins: + nginx_doc = "--nginx Use the Nginx plugin for authentication & installation" +else: + nginx_doc = "(nginx support is experimental, buggy, and not installed by default)" + # This is the short help for letsencrypt --help, where we disable argparse # altogether USAGE = SHORT_USAGE + """Choice of server plugins for obtaining and installing cert: --apache Use the Apache plugin for authentication & installation - --nginx Use the Nginx plugin for authentication & installation --standalone Run a standalone webserver for authentication + %s OR use different servers to obtain (authenticate) the cert and then install it: - --authenticator standalone --installer nginx + --authenticator standalone --installer apache More detailed help: @@ -84,7 +90,7 @@ More detailed help: all, automation, paths, security, testing, or any of the subcommands or plugins (certonly, install, nginx, apache, standalone, etc) -""" +""" % nginx_doc def _find_domains(args, installer): @@ -1061,7 +1067,7 @@ def main(cli_args=sys.argv[1:]): sys.excepthook = functools.partial(_handle_exception, args=None) # note: arg parser internally handles --help (and exits afterwards) - plugins = plugins_disco.PluginsRegistry.find_all() + global plugins args = prepare_and_parse_args(plugins, cli_args) config = configuration.NamespaceConfig(args) zope.component.provideUtility(config) From 01fabbe893128916d87e47bb5488e2c5314036b4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 26 Oct 2015 12:07:40 -0700 Subject: [PATCH 160/216] Also make apache docs conditional? --- letsencrypt/cli.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 9fa765a2e..61b62e320 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -69,6 +69,11 @@ if "nginx" in plugins: nginx_doc = "--nginx Use the Nginx plugin for authentication & installation" else: nginx_doc = "(nginx support is experimental, buggy, and not installed by default)" +if "apache" in plugins: + apache_doc = "--apache Use the Apache plugin for authentication & installation" +else: + apache_doc = "(the apache plugin is not installed)" + # This is the short help for letsencrypt --help, where we disable argparse From 60a0b01d5993a47f0db16752b986a3c95901e95c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 27 Oct 2015 11:30:02 -0700 Subject: [PATCH 161/216] Don't initialise plugins super early Also extra unit testing --- letsencrypt/cli.py | 36 ++++++++++++++++++----------------- letsencrypt/tests/cli_test.py | 16 ++++++++++++++-- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 61b62e320..c63cb3c5c 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -64,23 +64,11 @@ the cert. Major SUBCOMMANDS are: """ -plugins = plugins_disco.PluginsRegistry.find_all() -if "nginx" in plugins: - nginx_doc = "--nginx Use the Nginx plugin for authentication & installation" -else: - nginx_doc = "(nginx support is experimental, buggy, and not installed by default)" -if "apache" in plugins: - apache_doc = "--apache Use the Apache plugin for authentication & installation" -else: - apache_doc = "(the apache plugin is not installed)" - - - # This is the short help for letsencrypt --help, where we disable argparse # altogether USAGE = SHORT_USAGE + """Choice of server plugins for obtaining and installing cert: - --apache Use the Apache plugin for authentication & installation + %s --standalone Run a standalone webserver for authentication %s @@ -95,7 +83,20 @@ More detailed help: all, automation, paths, security, testing, or any of the subcommands or plugins (certonly, install, nginx, apache, standalone, etc) -""" % nginx_doc +""" + +def usage_strings(plugins): + """Make usage strings late so that plugins can be initialised late""" + print plugins + if "nginx" in plugins: + nginx_doc = "--nginx Use the Nginx plugin for authentication & installation" + else: + nginx_doc = "(nginx support is experimental, buggy, and not installed by default)" + if "apache" in plugins: + apache_doc = "--apache Use the Apache plugin for authentication & installation" + else: + apache_doc = "(the apache plugin is not installed)" + return USAGE % (apache_doc, nginx_doc), SHORT_USAGE def _find_domains(args, installer): @@ -633,8 +634,9 @@ class HelpfulArgumentParser(object): def __init__(self, args, plugins): plugin_names = [name for name, _p in plugins.iteritems()] self.help_topics = self.HELP_TOPICS + plugin_names + [None] + usage, short_usage = usage_strings(plugins) self.parser = configargparse.ArgParser( - usage=SHORT_USAGE, + usage=short_usage, formatter_class=argparse.ArgumentDefaultsHelpFormatter, args_for_setting_config_path=["-c", "--config"], default_config_files=flag_default("config_files")) @@ -651,7 +653,7 @@ class HelpfulArgumentParser(object): help_arg = max(help1, help2) if help_arg is True: # just --help with no topic; avoid argparse altogether - print USAGE + print usage sys.exit(0) self.visible_topics = self.determine_help_topics(help_arg) #print self.visible_topics @@ -1072,7 +1074,7 @@ def main(cli_args=sys.argv[1:]): sys.excepthook = functools.partial(_handle_exception, args=None) # note: arg parser internally handles --help (and exits afterwards) - global plugins + plugins = plugins_disco.PluginsRegistry.find_all() args = prepare_and_parse_args(plugins, cli_args) config = configuration.NamespaceConfig(args) zope.component.provideUtility(config) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index d8940e22c..3ef2ea161 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -69,30 +69,42 @@ class CLITest(unittest.TestCase): self.assertRaises(SystemExit, self._call, ['--help', 'all']) output = StringIO.StringIO() with mock.patch('letsencrypt.cli.sys.stdout', new=output): + plugins = disco.PluginsRegistry.find_all() self.assertRaises(SystemExit, self._call_stdout, ['--help', 'all']) out = output.getvalue() self.assertTrue("--configurator" in out) self.assertTrue("how a cert is deployed" in out) self.assertTrue("--manual-test-mode" in out) output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['-h', 'nginx']) out = output.getvalue() - if "nginx" in disco.PluginsRegistry.find_all(): + if "nginx" in plugins: # may be false while building distributions without plugins self.assertTrue("--nginx-ctl" in out) self.assertTrue("--manual-test-mode" not in out) self.assertTrue("--checkpoints" not in out) output.truncate(0) + + self.assertRaises(SystemExit, self._call_stdout, ['-h']) + out = output.getvalue() + if "nginx" in plugins: + self.assertTrue("Use the Nginx plugin" in out) + else: + self.assertTrue("(nginx support is experimental" in out) + output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['--help', 'plugins']) out = output.getvalue() self.assertTrue("--manual-test-mode" not in out) self.assertTrue("--prepare" in out) self.assertTrue("Plugin options" in out) output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['-h']) out = output.getvalue() from letsencrypt import cli - self.assertTrue(cli.USAGE in out) + self.assertTrue(cli.usage_strings(plugins)[0] in out) def test_configurator_selection(self): real_plugins = disco.PluginsRegistry.find_all() From 7e717a7a15f9107fd94bf26bd77016e61d38583a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 27 Oct 2015 11:49:25 -0700 Subject: [PATCH 162/216] lint --- letsencrypt/tests/cli_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 3ef2ea161..d8f6215dc 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -131,7 +131,6 @@ class CLITest(unittest.TestCase): self.assertTrue("NoInstallationError" in ret) with MockedVerb("auth") as mock_auth: - from letsencrypt import cli self._call(["certonly", "--standalone"]) self.assertEqual(1, mock_auth.call_count) From 8476efe86b7e45dabc018543b3baab8a0503ff41 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 28 Oct 2015 10:59:47 -0700 Subject: [PATCH 163/216] Inversion: make certonly the real verb, and "auth" an alias for it --- letsencrypt/cli.py | 27 +++++++++++++-------------- letsencrypt/tests/cli_test.py | 30 +++++++++++++++--------------- 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index c63cb3c5c..db3cd051a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -87,7 +87,6 @@ More detailed help: def usage_strings(plugins): """Make usage strings late so that plugins can be initialised late""" - print plugins if "nginx" in plugins: nginx_doc = "--nginx Use the Nginx plugin for authentication & installation" else: @@ -391,7 +390,7 @@ def choose_configurator_plugins(args, config, plugins, verb): # Which plugins do we need? need_inst = need_auth = (verb == "run") - if verb == "auth": + if verb == "certonly": need_auth = True if verb == "install": need_inst = True @@ -461,7 +460,7 @@ def run(args, config, plugins): # pylint: disable=too-many-branches,too-many-lo display_ops.success_renewal(domains) -def auth(args, config, plugins): +def obtaincert(args, config, plugins): """Authenticate & obtain cert, but do not install it.""" if args.domains is not None and args.csr is not None: @@ -471,7 +470,7 @@ def auth(args, config, plugins): try: # installers are used in auth mode to determine domain names - installer, authenticator = choose_configurator_plugins(args, config, plugins, "auth") + installer, authenticator = choose_configurator_plugins(args, config, plugins, "certonly") except PluginSelectionError, e: return e.message @@ -495,7 +494,7 @@ def install(args, config, plugins): # XXX: Update for renewer/RenewableCert try: - installer, _ = choose_configurator_plugins(args, config, plugins, "auth") + installer, _ = choose_configurator_plugins(args, config, plugins, "certonly") except PluginSelectionError, e: return e.message @@ -623,7 +622,7 @@ class HelpfulArgumentParser(object): """ # Maps verbs/subcommands to the functions that implement them - VERBS = {"auth": auth, "certonly": auth, "config_changes": config_changes, + VERBS = {"auth": obtaincert, "certonly": obtaincert, "config_changes": config_changes, "install": install, "plugins": plugins_cmd, "revoke": revoke, "rollback": rollback, "run": run} @@ -685,8 +684,8 @@ class HelpfulArgumentParser(object): for i, token in enumerate(self.args): if token in self.VERBS: verb = token - if verb == "certonly": - verb = "auth" + if verb == "auth": + verb = "certonly" if verb == "everything": verb = "run" self.verb = verb @@ -774,8 +773,8 @@ class HelpfulArgumentParser(object): """ # topics maps each topic to whether it should be documented by # argparse on the command line - if chosen_topic == "certonly": - chosen_topic = "auth" + if chosen_topic == "auth": + chosen_topic = "certonly" if chosen_topic == "everything": chosen_topic = "run" if chosen_topic == "all": @@ -884,13 +883,13 @@ def prepare_and_parse_args(plugins, args): def _create_subparsers(helpful): - helpful.add_group("auth", description="Options for modifying how a cert is obtained") + helpful.add_group("certonly", description="Options for modifying how a cert is obtained") helpful.add_group("install", description="Options for modifying how a cert is deployed") helpful.add_group("revoke", description="Options for revocation of certs") helpful.add_group("rollback", description="Options for reverting config changes") helpful.add_group("plugins", description="Plugin options") - helpful.add("auth", + helpful.add("certonly", "--csr", type=read_file, help="Path to a Certificate Signing Request (CSR) in DER" " format; note that the .csr file *must* contain a Subject" @@ -918,7 +917,7 @@ def _paths_parser(helpful): "paths", description="Arguments changing execution paths & servers") cph = "Path to where cert is saved (with auth), installed (with install --csr) or revoked." - if verb == "auth": + if verb == "certonly": add("paths", "--cert-path", default=flag_default("auth_cert_path"), help=cph) elif verb == "revoke": add("paths", "--cert-path", type=read_file, required=True, help=cph) @@ -931,7 +930,7 @@ def _paths_parser(helpful): help="Path to private key for cert creation or revocation (if account key is missing)") default_cp = None - if verb == "auth": + if verb == "certonly": default_cp = flag_default("auth_chain_path") add("paths", "--fullchain-path", default=default_cp, help="Accompanying path to a full certificate chain (cert plus chain).") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index d8f6215dc..e7062f675 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -130,8 +130,8 @@ class CLITest(unittest.TestCase): self.assertTrue("Could not find configuration root" in ret) self.assertTrue("NoInstallationError" in ret) - with MockedVerb("auth") as mock_auth: - self._call(["certonly", "--standalone"]) + with MockedVerb("certonly") as mock_certonly: + self._call(["auth", "--standalone"]) self.assertEqual(1, mock_auth.call_count) def test_rollback(self): @@ -153,16 +153,16 @@ class CLITest(unittest.TestCase): for r in xrange(len(flags)))): self._call(['plugins'] + list(args)) - def test_auth_bad_args(self): - ret, _, _, _ = self._call(['-d', 'foo.bar', 'auth', '--csr', CSR]) + def test_certonly_bad_args(self): + ret, _, _, _ = self._call(['-d', 'foo.bar', 'certonly', '--csr', CSR]) self.assertEqual(ret, '--domains and --csr are mutually exclusive') - ret, _, _, _ = self._call(['-a', 'bad_auth', 'auth']) + ret, _, _, _ = self._call(['-a', 'bad_auth', 'certonly']) self.assertEqual(ret, 'The requested bad_auth plugin does not appear to be installed') @mock.patch('letsencrypt.crypto_util.notAfter') @mock.patch('letsencrypt.cli.zope.component.getUtility') - def test_auth_new_request_success(self, mock_get_utility, mock_notAfter): + def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter): cert_path = '/etc/letsencrypt/live/foo.bar' date = '1970-01-01' mock_notAfter().date.return_value = date @@ -170,7 +170,7 @@ class CLITest(unittest.TestCase): mock_lineage = mock.MagicMock(cert=cert_path, fullchain=cert_path) mock_client = mock.MagicMock() mock_client.obtain_and_enroll_certificate.return_value = mock_lineage - self._auth_new_request_common(mock_client) + self._certonly_new_request_common(mock_client) self.assertEqual( mock_client.obtain_and_enroll_certificate.call_count, 1) self.assertTrue( @@ -178,23 +178,23 @@ class CLITest(unittest.TestCase): self.assertTrue( date in mock_get_utility().add_message.call_args[0][0]) - def test_auth_new_request_failure(self): + def test_certonly_new_request_failure(self): mock_client = mock.MagicMock() mock_client.obtain_and_enroll_certificate.return_value = False self.assertRaises(errors.Error, - self._auth_new_request_common, mock_client) + self._certonly_new_request_common, mock_client) - def _auth_new_request_common(self, mock_client): + def _certonly_new_request_common(self, mock_client): with mock.patch('letsencrypt.cli._treat_as_renewal') as mock_renewal: mock_renewal.return_value = None with mock.patch('letsencrypt.cli._init_le_client') as mock_init: mock_init.return_value = mock_client - self._call(['-d', 'foo.bar', '-a', 'standalone', 'auth']) + self._call(['-d', 'foo.bar', '-a', 'standalone', 'certonly']) @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._treat_as_renewal') @mock.patch('letsencrypt.cli._init_le_client') - def test_auth_renewal(self, mock_init, mock_renewal, mock_get_utility): + def test_certonly_renewal(self, mock_init, mock_renewal, mock_get_utility): cert_path = '/etc/letsencrypt/live/foo.bar/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' @@ -208,7 +208,7 @@ class CLITest(unittest.TestCase): mock_init.return_value = mock_client with mock.patch('letsencrypt.cli.OpenSSL'): with mock.patch('letsencrypt.cli.crypto_util'): - self._call(['-d', 'foo.bar', '-a', 'standalone', 'auth']) + self._call(['-d', 'foo.bar', '-a', 'standalone', 'certonly']) mock_client.obtain_certificate.assert_called_once_with(['foo.bar']) self.assertEqual(mock_lineage.save_successor.call_count, 1) mock_lineage.update_all_links_to.assert_called_once_with( @@ -220,7 +220,7 @@ class CLITest(unittest.TestCase): @mock.patch('letsencrypt.cli.display_ops.pick_installer') @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._init_le_client') - def test_auth_csr(self, mock_init, mock_get_utility, + def test_certonly_csr(self, mock_init, mock_get_utility, mock_pick_installer, mock_notAfter): cert_path = '/etc/letsencrypt/live/blahcert.pem' date = '1970-01-01' @@ -234,7 +234,7 @@ class CLITest(unittest.TestCase): installer = 'installer' self._call( - ['-a', 'standalone', '-i', installer, 'auth', '--csr', CSR, + ['-a', 'standalone', '-i', installer, 'certonly', '--csr', CSR, '--cert-path', cert_path, '--fullchain-path', '/', '--chain-path', '/']) self.assertEqual(mock_pick_installer.call_args[0][1], installer) From 28071a3e72a2d00e6d5ad82ba1c3028fa72ff4ca Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 28 Oct 2015 11:06:56 -0700 Subject: [PATCH 164/216] Fix stray ref --- letsencrypt/tests/cli_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index e7062f675..669fa1ce8 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -132,7 +132,7 @@ class CLITest(unittest.TestCase): with MockedVerb("certonly") as mock_certonly: self._call(["auth", "--standalone"]) - self.assertEqual(1, mock_auth.call_count) + self.assertEqual(1, mock_certonly.call_count) def test_rollback(self): _, _, _, client = self._call(['rollback']) From 0d12ded6febb9912491b1daa44e8c07426107cc9 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 28 Oct 2015 18:55:52 -0700 Subject: [PATCH 165/216] Alias "everything" correctly --- letsencrypt/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index db3cd051a..641d0d341 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -622,7 +622,8 @@ class HelpfulArgumentParser(object): """ # Maps verbs/subcommands to the functions that implement them - VERBS = {"auth": obtaincert, "certonly": obtaincert, "config_changes": config_changes, + VERBS = {"auth": obtaincert, "certonly": obtaincert, + "config_changes": config_changes, "everything": run, "install": install, "plugins": plugins_cmd, "revoke": revoke, "rollback": rollback, "run": run} From e9661c9634a2dc9ce28afae0401f427db7519889 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 28 Oct 2015 19:57:56 -0700 Subject: [PATCH 166/216] Fixed tests --- letsencrypt/plugins/disco_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/disco_test.py b/letsencrypt/plugins/disco_test.py index 773dbe0a1..41d8cd5fe 100644 --- a/letsencrypt/plugins/disco_test.py +++ b/letsencrypt/plugins/disco_test.py @@ -51,7 +51,7 @@ class PluginEntryPointTest(unittest.TestCase): def test_description(self): self.assertEqual( - "Automatically configure and run a simple webserver", + "Automatically use a temporary webserver", self.plugin_ep.description) def test_description_with_name(self): From 7fdea8dd1ae951bc1d6348a90b25cfa5148e9081 Mon Sep 17 00:00:00 2001 From: Axel Beckert Date: Thu, 29 Oct 2015 09:19:38 +0100 Subject: [PATCH 167/216] Use git instead of git-core in bootstrapping on Debian and friends Fixes #1179. --- bootstrap/_deb_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_deb_common.sh b/bootstrap/_deb_common.sh index 2f44c4e91..71144ce40 100755 --- a/bootstrap/_deb_common.sh +++ b/bootstrap/_deb_common.sh @@ -33,7 +33,7 @@ if apt-cache show python-virtualenv > /dev/null ; then fi apt-get install -y --no-install-recommends \ - git-core \ + git \ python \ python-dev \ $virtualenv \ From c3fbed1f81484daf1cb440b55ca3dc2f0af55ee7 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 29 Oct 2015 08:19:54 +0000 Subject: [PATCH 168/216] Offline unittest v2. Supersedes https://github.com/letsencrypt/letsencrypt/pull/1183. --- acme/acme/challenges_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index ed44d4c45..2a12b4a64 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -359,9 +359,9 @@ class DVSNIResponseTest(unittest.TestCase): cert=mock.sentinel.cert)) mock_verify_cert.assert_called_once_with(self.msg, mock.sentinel.cert) - def test_simple_verify_false_on_probe_error(self): - chall = mock.Mock() - chall.probe_cert.side_effect = errors.Error + @mock.patch('acme.challenges.DVSNIResponse.probe_cert') + def test_simple_verify_false_on_probe_error(self, mock_probe_cert): + mock_probe_cert.side_effect = errors.Error self.assertFalse(self.msg.simple_verify( self.chall, self.domain, self.key.public_key())) From 39c83d17d7adb790ec68658a957481c73f63e7e0 Mon Sep 17 00:00:00 2001 From: David Kreitschmann Date: Thu, 29 Oct 2015 10:06:32 +0100 Subject: [PATCH 169/216] Add -t to apache2ctl -D DUMP_RUN_CFG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Calling apache2ctl -D DUMP_RUN_CFG on Debian Wheezy (Apache 2.2) will open a socket which breaks standalone auth. letsencrypt still won’t work with apache 2.2 because it only returns „Syntax OK“. Apache 2.4 works the same with or without -t. --- letsencrypt-apache/letsencrypt_apache/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/letsencrypt-apache/letsencrypt_apache/parser.py index 0a3643064..ec5211ae4 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/letsencrypt-apache/letsencrypt_apache/parser.py @@ -122,7 +122,7 @@ class ApacheParser(object): """ try: proc = subprocess.Popen( - [ctl, "-D", "DUMP_RUN_CFG"], + [ctl, "-t", "-D", "DUMP_RUN_CFG"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = proc.communicate() From faa61da2a6e98b393c38ce13f4376280f8f73820 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 29 Oct 2015 10:46:39 -0700 Subject: [PATCH 170/216] manual_description += kuba_feedback --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index eba53f696..459d771ce 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -37,7 +37,7 @@ class Authenticator(common.Plugin): zope.interface.classProvides(interfaces.IPluginFactory) hidden = True - description = "Manually configure and run a simple Python webserver" + description = "Manually edit your server configuration" MESSAGE_TEMPLATE = """\ Make sure your web server displays the following content at From de30a28555f9fec2dce56c38ad234c5fd4ec6839 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 29 Oct 2015 13:20:52 -0700 Subject: [PATCH 171/216] Another shot at a description --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 459d771ce..b3f41d1f2 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -37,7 +37,7 @@ class Authenticator(common.Plugin): zope.interface.classProvides(interfaces.IPluginFactory) hidden = True - description = "Manually edit your server configuration" + description = "Manually configure an HTTP server" MESSAGE_TEMPLATE = """\ Make sure your web server displays the following content at From 4cc06106799dafc6b525e433a5d12f61445cce88 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 29 Oct 2015 20:46:43 +0000 Subject: [PATCH 172/216] Remove serve_forever2/shutdown2 (reduces probability of #1085). I'm not even sure why `serve_forever2` and `shutdown2` were introduced in the first place... It probably follows from my misconception about the SocketServer module. After having studied the module again, I come to the conclusion that we can get rid of my crap, simultanously reducing probability of #1085 (hopefully down to 0)! `server_forever` is used throughout tests instead of `handle_request`, because `shutdown`, following docs, "must be called while serve_forever() is running in another thread, or it will deadlock", and our `probe_sni` HTTP request is already enough to kill single `handle_request`. We don't need to use any busy waiting block or `sleep` between serve and shutdown; studying CPython source code leads to the conclusion that the following construction is non-blocking: ```python import threading, SocketServer s = SocketServer.TCPServer(("", 0), None) t = threading.Thread(target=s.shutdown) t.start() s.serve_forever() # returns immediately t.join() # returns immediately ``` --- acme/acme/standalone.py | 28 ------------- acme/acme/standalone_test.py | 65 +++---------------------------- letsencrypt/plugins/standalone.py | 6 ++- 3 files changed, 10 insertions(+), 89 deletions(-) diff --git a/acme/acme/standalone.py b/acme/acme/standalone.py index 49759bc07..310e61995 100644 --- a/acme/acme/standalone.py +++ b/acme/acme/standalone.py @@ -4,7 +4,6 @@ import collections import functools import logging import os -import socket import sys import six @@ -50,37 +49,11 @@ class ACMEServerMixin: # pylint: disable=old-style-class server_version = "ACME client standalone challenge solver" allow_reuse_address = True - def __init__(self): - self._stopped = False - - def serve_forever2(self): - """Serve forever, until other thread calls `shutdown2`.""" - logger.debug("Starting server at %s:%d...", - *self.socket.getsockname()[:2]) - while not self._stopped: - self.handle_request() - - def shutdown2(self): - """Shutdown server loop from `serve_forever2`.""" - self._stopped = True - - # dummy request to terminate last server_forever2.handle_request() - sock = socket.socket() - try: - sock.connect(self.socket.getsockname()) - except socket.error: - pass # thread is probably already finished - finally: - sock.close() - - self.server_close() - class DVSNIServer(TLSServer, ACMEServerMixin): """DVSNI Server.""" def __init__(self, server_address, certs): - ACMEServerMixin.__init__(self) TLSServer.__init__( self, server_address, socketserver.BaseRequestHandler, certs=certs) @@ -89,7 +62,6 @@ class SimpleHTTPServer(BaseHTTPServer.HTTPServer, ACMEServerMixin): """SimpleHTTP Server.""" def __init__(self, server_address, resources): - ACMEServerMixin.__init__(self) BaseHTTPServer.HTTPServer.__init__( self, server_address, SimpleHTTPRequestHandler.partial_init( simple_http_resources=resources)) diff --git a/acme/acme/standalone_test.py b/acme/acme/standalone_test.py index 349581a3d..ed015b826 100644 --- a/acme/acme/standalone_test.py +++ b/acme/acme/standalone_test.py @@ -1,7 +1,6 @@ """Tests for acme.standalone.""" import os import shutil -import socket import threading import tempfile import time @@ -29,54 +28,6 @@ class TLSServerTest(unittest.TestCase): server.server_close() # pylint: disable=no-member -class ACMEServerMixinTest(unittest.TestCase): - """Tests for acme.standalone.ACMEServerMixin.""" - - def setUp(self): - from acme.standalone import ACMEServerMixin - - class _MockHandler(socketserver.BaseRequestHandler): - # pylint: disable=missing-docstring,no-member,no-init - - def handle(self): - self.request.sendall(b"DONE") - - class _MockServer(socketserver.TCPServer, ACMEServerMixin): - def __init__(self, *args, **kwargs): - socketserver.TCPServer.__init__(self, *args, **kwargs) - ACMEServerMixin.__init__(self) - - self.server = _MockServer(("", 0), _MockHandler) - - def _busy_wait(self): # pragma: no cover - # This function is used to avoid race conditions in tests, but - # not all of the functionality is always used, hence "no - # cover" - while True: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - # pylint: disable=no-member - sock.connect(self.server.socket.getsockname()) - except socket.error: - pass - else: - sock.recv(4) # wait until handle_request is actually called - break - finally: - sock.close() - time.sleep(1) - - def test_serve_shutdown(self): - thread = threading.Thread(target=self.server.serve_forever2) - thread.start() - self._busy_wait() - self.server.shutdown2() - - def test_shutdown2_not_running(self): - self.server.shutdown2() - self.server.shutdown2() - - class DVSNIServerTest(unittest.TestCase): """Test for acme.standalone.DVSNIServer.""" @@ -89,20 +40,16 @@ class DVSNIServerTest(unittest.TestCase): from acme.standalone import DVSNIServer self.server = DVSNIServer(("", 0), certs=self.certs) # pylint: disable=no-member - self.thread = threading.Thread(target=self.server.handle_request) + self.thread = threading.Thread(target=self.server.serve_forever) self.thread.start() def tearDown(self): - self.server.shutdown2() + self.server.shutdown() # pylint: disable=no-member self.thread.join() - def test_init(self): - # pylint: disable=protected-access - self.assertFalse(self.server._stopped) - - def test_dvsni(self): + def test_it(self): host, port = self.server.socket.getsockname()[:2] - cert = crypto_util.probe_sni(b'localhost', host=host, port=port) + cert = crypto_util.probe_sni(b'localhost', host=host, port=port, timeout=1) self.assertEqual(jose.ComparableX509(cert), jose.ComparableX509(self.certs[b'localhost'][1])) @@ -120,11 +67,11 @@ class SimpleHTTPServerTest(unittest.TestCase): # pylint: disable=no-member self.port = self.server.socket.getsockname()[1] - self.thread = threading.Thread(target=self.server.handle_request) + self.thread = threading.Thread(target=self.server.serve_forever) self.thread.start() def tearDown(self): - self.server.shutdown2() + self.server.shutdown() # pylint: disable=no-member self.thread.join() def test_index(self): diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py index 1b56fbdbc..ccff03319 100644 --- a/letsencrypt/plugins/standalone.py +++ b/letsencrypt/plugins/standalone.py @@ -72,7 +72,9 @@ class ServerManager(object): except socket.error as error: raise errors.StandaloneBindError(error, port) - thread = threading.Thread(target=server.serve_forever2) + thread = threading.Thread( + # pylint: disable=no-member + target=server.serve_forever) thread.start() # if port == 0, then random free port on OS is taken @@ -90,7 +92,7 @@ class ServerManager(object): instance = self._instances[port] logger.debug("Stopping server at %s:%d...", *instance.server.socket.getsockname()[:2]) - instance.server.shutdown2() + instance.server.shutdown() instance.thread.join() del self._instances[port] From 724d06eec5845fe2adb4d61ed6858fb305dd74a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mantas=20Mikul=C4=97nas?= Date: Fri, 30 Oct 2015 17:11:55 +0200 Subject: [PATCH 173/216] bootstrap: use a proper dependency test for Arch `pacman -T` exists for this exact purpose; it respects provides without having to manually code them into the script. --- bootstrap/_arch_common.sh | 42 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/bootstrap/_arch_common.sh b/bootstrap/_arch_common.sh index 6895addf4..f66067ffb 100755 --- a/bootstrap/_arch_common.sh +++ b/bootstrap/_arch_common.sh @@ -1,29 +1,27 @@ #!/bin/sh # Tested with: -# - Manjaro 15.09 (x86_64) # - ArchLinux (x86_64) - -# Both "gcc-multilib" and "gcc" packages provide gcc. If user already has -# "gcc-multilib" installed, let's stick to their choice -if pacman -Qc gcc-multilib &>/dev/null -then - GCC_PACKAGE="gcc-multilib"; -else - GCC_PACKAGE="gcc"; -fi - +# # "python-virtualenv" is Python3, but "python2-virtualenv" provides # only "virtualenv2" binary, not "virtualenv" necessary in # ./bootstrap/dev/_common_venv.sh -pacman -S --needed \ - git \ - python2 \ - python-virtualenv \ - "$GCC_PACKAGE" \ - dialog \ - augeas \ - openssl \ - libffi \ - ca-certificates \ - pkg-config \ + +deps=" + git + python2 + python-virtualenv + gcc + dialog + augeas + openssl + libffi + ca-certificates + pkg-config +" + +missing=$(pacman -T $deps) + +if [ "$missing" ]; then + pacman -S --needed $missing +fi From edabce9a96bd2a91a84e6f91d5e729808d4ceea4 Mon Sep 17 00:00:00 2001 From: Steve Desmond Date: Fri, 30 Oct 2015 14:47:23 -0400 Subject: [PATCH 174/216] improved language consistency in error/help messages --- letsencrypt/auth_handler.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 5f6365167..4f2a6fa3e 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -486,29 +486,29 @@ def is_preferred(offered_challb, satisfied, _ERROR_HELP_COMMON = ( "To fix these errors, please make sure that your domain name was entered " - "correctly and the DNS A record(s) for that domain contains the " + "correctly and the DNS A record(s) for that domain contain(s) the " "right IP address.") _ERROR_HELP = { "connection": _ERROR_HELP_COMMON + " Additionally, please check that your computer " - "has publicly routable IP address and no firewalls are preventing the " - "server from communicating with the client.", + "has a publicly routable IP address and that no firewalls are preventing " + "the server from communicating with the client.", "dnssec": _ERROR_HELP_COMMON + " Additionally, if you have DNSSEC enabled for " - "your domain, please ensure the signature is valid.", + "your domain, please ensure that the signature is valid.", "malformed": "To fix these errors, please make sure that you did not provide any " - "invalid information to the client and try running Let's Encrypt " + "invalid information to the client, and try running Let's Encrypt " "again.", "serverInternal": "Unfortunately, an error on the ACME server prevented you from completing " "authorization. Please try again later.", "tls": - _ERROR_HELP_COMMON + " Additionally, please check that you have an up " - "to date TLS configuration that allows the server to communicate with " - "the Let's Encrypt client.", + _ERROR_HELP_COMMON + " Additionally, please check that you have an " + "up-to-date TLS configuration that allows the server to communicate " + "with the Let's Encrypt client.", "unauthorized": _ERROR_HELP_COMMON, "unknownHost": _ERROR_HELP_COMMON, } From 40706e294707e4b864df25f3122a5999ffce1095 Mon Sep 17 00:00:00 2001 From: Steve Desmond Date: Fri, 30 Oct 2015 14:52:36 -0400 Subject: [PATCH 175/216] add "--" to CLI arg for consistency --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 4f488e2a2..6a3c39e54 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -128,7 +128,7 @@ Plugin A I Notes and status ========== = = ================================================================ standalone Y N Very stable. Uses port 80 (force by ``--standalone-supported-challenges simpleHttp``) or 443 - (force by ``standalone-supported-challenges dvsni``). + (force by ``--standalone-supported-challenges dvsni``). apache Y Y Alpha. Automates Apache installation, works fairly well but on Debian-based distributions only for now. webroot Y N Works with already running webserver, by writing necessary files From 537fcf581c267e1bb2628adc5363d97746772bde Mon Sep 17 00:00:00 2001 From: Alexander Mankuta Date: Fri, 30 Oct 2015 21:17:02 +0200 Subject: [PATCH 176/216] Add Gentoo bootstrapping Includes support for all three major package managers. --- bootstrap/_gentoo_common.sh | 23 +++++++++++++++++++++++ bootstrap/gentoo.sh | 1 + bootstrap/install-deps.sh | 3 +++ letsencrypt-auto | 3 +++ 4 files changed, 30 insertions(+) create mode 100755 bootstrap/_gentoo_common.sh create mode 120000 bootstrap/gentoo.sh diff --git a/bootstrap/_gentoo_common.sh b/bootstrap/_gentoo_common.sh new file mode 100755 index 000000000..a718db7ff --- /dev/null +++ b/bootstrap/_gentoo_common.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +PACKAGES="dev-vcs/git + dev-lang/python:2.7 + dev-python/virtualenv + dev-util/dialog + app-admin/augeas + dev-libs/openssl + dev-libs/libffi + app-misc/ca-certificates + virtual/pkgconfig" + +case "$PACKAGE_MANAGER" in + (paludis) + cave resolve --keep-targets if-possible $PACKAGES -x + ;; + (pkgcore) + pmerge --noreplace $PACKAGES + ;; + (portage|*) + emerge --noreplace $PACKAGES + ;; +esac diff --git a/bootstrap/gentoo.sh b/bootstrap/gentoo.sh new file mode 120000 index 000000000..125d6a592 --- /dev/null +++ b/bootstrap/gentoo.sh @@ -0,0 +1 @@ +_gentoo_common.sh \ No newline at end of file diff --git a/bootstrap/install-deps.sh b/bootstrap/install-deps.sh index c159858c5..3cb0fc274 100755 --- a/bootstrap/install-deps.sh +++ b/bootstrap/install-deps.sh @@ -23,6 +23,9 @@ elif [ -f /etc/arch-release ] ; then elif [ -f /etc/redhat-release ] ; then echo "Bootstrapping dependencies for RedHat-based OSes..." $SUDO $BOOTSTRAP/_rpm_common.sh +elif [ -f /etc/gentoo-release ] ; then + echo "Bootstrapping dependencies for Gentoo-based OSes..." + $SUDO $BOOTSTRAP/_gentoo_common.sh elif uname | grep -iq FreeBSD ; then echo "Bootstrapping dependencies for FreeBSD..." $SUDO $BOOTSTRAP/freebsd.sh diff --git a/letsencrypt-auto b/letsencrypt-auto index 769f5a1d9..aba8baec1 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -47,6 +47,9 @@ then elif [ -f /etc/redhat-release ] ; then echo "Bootstrapping dependencies for RedHat-based OSes..." $SUDO $BOOTSTRAP/_rpm_common.sh + elif [ -f /etc/gentoo-release ] ; then + echo "Bootstrapping dependencies for Gentoo-based OSes..." + $SUDO $BOOTSTRAP/_gentoo_common.sh elif uname | grep -iq FreeBSD ; then echo "Bootstrapping dependencies for FreeBSD..." $SUDO $BOOTSTRAP/freebsd.sh From 20ae2debe45ce615f1948786ee58a805b48293c3 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Fri, 30 Oct 2015 20:50:55 +0000 Subject: [PATCH 177/216] Docs: --a -> -a (fixes #1217) --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 6a3c39e54..73c3fe140 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -135,7 +135,7 @@ webroot Y N Works with already running webserver, by writing necessary files to the disk (``--webroot-path`` should be pointed to your ``public_html``). Currently, when multiple domains are specified (`-d`), they must all use the same web root path. -manual Y N Hidden from standard UI, use with ``--a manual``. Requires to +manual Y N Hidden from standard UI, use with ``-a manual``. Requires to copy and paste commands into a new terminal session. Allows to run client on machine different than target webserver, e.g. your laptop. From 4e62a4bfe2465817c1b47a59c2cef00d98eac333 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 30 Oct 2015 13:54:46 -0700 Subject: [PATCH 178/216] fixes #1227 --- letsencrypt/cli.py | 4 +++- letsencrypt/tests/cli_test.py | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 641d0d341..fdfa5bbfb 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -85,6 +85,7 @@ More detailed help: plugins (certonly, install, nginx, apache, standalone, etc) """ + def usage_strings(plugins): """Make usage strings late so that plugins can be initialised late""" if "nginx" in plugins: @@ -494,7 +495,8 @@ def install(args, config, plugins): # XXX: Update for renewer/RenewableCert try: - installer, _ = choose_configurator_plugins(args, config, plugins, "certonly") + installer, _ = choose_configurator_plugins(args, config, + plugins, "install") except PluginSelectionError, e: return e.message diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 669fa1ce8..55946e7aa 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -106,6 +106,12 @@ class CLITest(unittest.TestCase): from letsencrypt import cli self.assertTrue(cli.usage_strings(plugins)[0] in out) + @mock.patch('letsencrypt.cli.display_ops') + def test_installer_selection(self, mock_display_ops): + self._call(['install', '--domain', 'foo.bar', '--cert-path', 'cert', + '--key-path', 'key', '--chain-path', 'chain']) + self.assertEqual(mock_display_ops.pick_installer.call_count, 1) + def test_configurator_selection(self): real_plugins = disco.PluginsRegistry.find_all() args = ['--agree-dev-preview', '--apache', @@ -221,7 +227,7 @@ class CLITest(unittest.TestCase): @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._init_le_client') def test_certonly_csr(self, mock_init, mock_get_utility, - mock_pick_installer, mock_notAfter): + mock_pick_installer, mock_notAfter): cert_path = '/etc/letsencrypt/live/blahcert.pem' date = '1970-01-01' mock_notAfter().date.return_value = date From fa7aed4d63184e0e8a428cb03ae5c0794103e06e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 30 Oct 2015 14:44:17 -0700 Subject: [PATCH 179/216] Ensure that mandatory flags are displayed under the relevant verb help topics Closes: #996 In part our problem was trying to pick a single topic ("paths") for flags that become essential in some cases. But we were also calling _paths_parser before all the topic groups had been created. --- letsencrypt/cli.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 641d0d341..a2d3652b0 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -650,12 +650,12 @@ class HelpfulArgumentParser(object): help1 = self.prescan_for_flag("-h", self.help_topics) help2 = self.prescan_for_flag("--help", self.help_topics) assert max(True, "a") == "a", "Gravity changed direction" - help_arg = max(help1, help2) - if help_arg is True: + self.help_arg = max(help1, help2) + if self.help_arg is True: # just --help with no topic; avoid argparse altogether print usage sys.exit(0) - self.visible_topics = self.determine_help_topics(help_arg) + self.visible_topics = self.determine_help_topics(self.help_arg) #print self.visible_topics self.groups = {} # elements are added by .add_group() @@ -873,12 +873,12 @@ def prepare_and_parse_args(plugins, args): help="Require that all configuration files are owned by the current " "user; only needed if your config is somewhere unsafe like /tmp/") + _create_subparsers(helpful) _paths_parser(helpful) # _plugins_parsing should be the last thing to act upon the main # parser (--help should display plugin-specific options last) _plugins_parsing(helpful, plugins) - _create_subparsers(helpful) return helpful.parse_args() @@ -914,19 +914,28 @@ def _create_subparsers(helpful): def _paths_parser(helpful): add = helpful.add verb = helpful.verb + if verb == "help": + verb = helpful.help_arg helpful.add_group( "paths", description="Arguments changing execution paths & servers") - cph = "Path to where cert is saved (with auth), installed (with install --csr) or revoked." + cph = "Path to where cert is saved (with auth --csr), installed from or revoked." + section = "paths" + if verb in ("install", "revoke", "certonly"): + section = verb if verb == "certonly": - add("paths", "--cert-path", default=flag_default("auth_cert_path"), help=cph) + add(section, "--cert-path", default=flag_default("auth_cert_path"), help=cph) elif verb == "revoke": - add("paths", "--cert-path", type=read_file, required=True, help=cph) + add(section, "--cert-path", type=read_file, required=True, help=cph) else: - add("paths", "--cert-path", help=cph, required=(verb == "install")) + add(section, "--cert-path", help=cph, required=(verb == "install")) + section = "paths" + if verb in ("install", "revoke"): + section = verb + print helpful.help_arg, helpful.help_arg == "install" # revoke --key-path reads a file, install --key-path takes a string - add("paths", "--key-path", type=((verb == "revoke" and read_file) or str), + add(section, "--key-path", type=((verb == "revoke" and read_file) or str), required=(verb == "install"), help="Path to private key for cert creation or revocation (if account key is missing)") From 3356ce75586ff79c6f6d4e24f0645b5269464c0b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 30 Oct 2015 14:55:51 -0700 Subject: [PATCH 180/216] This is a pretty silly lint warning that we're hitting a lot --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index 268d61ec6..e882d0858 100644 --- a/.pylintrc +++ b/.pylintrc @@ -38,7 +38,7 @@ load-plugins=linter_plugin # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=fixme,locally-disabled,abstract-class-not-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name +disable=fixme,locally-disabled,abstract-class-not-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes # abstract-class-not-used cannot be disabled locally (at least in pylint 1.4.1) From e4b10f76f9e491f0008ea7a10b8ffe05f62f85a9 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 30 Oct 2015 14:56:08 -0700 Subject: [PATCH 181/216] Delint --- letsencrypt/cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index a2d3652b0..3477e4eb4 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -915,14 +915,14 @@ def _paths_parser(helpful): add = helpful.add verb = helpful.verb if verb == "help": - verb = helpful.help_arg + verb = helpful.help_arg helpful.add_group( "paths", description="Arguments changing execution paths & servers") cph = "Path to where cert is saved (with auth --csr), installed from or revoked." section = "paths" if verb in ("install", "revoke", "certonly"): - section = verb + section = verb if verb == "certonly": add(section, "--cert-path", default=flag_default("auth_cert_path"), help=cph) elif verb == "revoke": @@ -932,7 +932,7 @@ def _paths_parser(helpful): section = "paths" if verb in ("install", "revoke"): - section = verb + section = verb print helpful.help_arg, helpful.help_arg == "install" # revoke --key-path reads a file, install --key-path takes a string add(section, "--key-path", type=((verb == "revoke" and read_file) or str), From f00280f71adda2fb3957c5c3a35144fb41988853 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 30 Oct 2015 15:12:54 -0700 Subject: [PATCH 182/216] Testiness --- letsencrypt/tests/cli_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 669fa1ce8..2f12f055d 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -101,6 +101,24 @@ class CLITest(unittest.TestCase): self.assertTrue("Plugin options" in out) output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['--help', 'install']) + out = output.getvalue() + self.assertTrue("--cert-path" in out) + self.assertTrue("--key-path" in out) + output.truncate(0) + + self.assertRaises(SystemExit, self._call_stdout, ['--help', 'revoke']) + out = output.getvalue() + self.assertTrue("--cert-path" in out) + self.assertTrue("--key-path" in out) + output.truncate(0) + + self.assertRaises(SystemExit, self._call_stdout, ['--help', 'config_changes']) + out = output.getvalue() + self.assertTrue("--cert-path" not in out) + self.assertTrue("--key-path" not in out) + output.truncate(0) + self.assertRaises(SystemExit, self._call_stdout, ['-h']) out = output.getvalue() from letsencrypt import cli From a5e815008e43bfd904e2bc73469a20c6604192fe Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 30 Oct 2015 15:22:05 -0700 Subject: [PATCH 183/216] Factor out all the stdout wrangling from help tests --- letsencrypt/tests/cli_test.py | 88 ++++++++++++++++------------------- 1 file changed, 40 insertions(+), 48 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 2f12f055d..e38448249 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -64,65 +64,57 @@ class CLITest(unittest.TestCase): self._call([]) self.assertEqual(1, mock_run.call_count) - def test_help(self): - self.assertRaises(SystemExit, self._call, ['--help']) - self.assertRaises(SystemExit, self._call, ['--help', 'all']) + def _help_output(self, args): + "Run a help command, and return the help string for scrutiny" output = StringIO.StringIO() with mock.patch('letsencrypt.cli.sys.stdout', new=output): plugins = disco.PluginsRegistry.find_all() - self.assertRaises(SystemExit, self._call_stdout, ['--help', 'all']) + self.assertRaises(SystemExit, self._call_stdout, args) out = output.getvalue() - self.assertTrue("--configurator" in out) - self.assertTrue("how a cert is deployed" in out) - self.assertTrue("--manual-test-mode" in out) - output.truncate(0) + return out - self.assertRaises(SystemExit, self._call_stdout, ['-h', 'nginx']) - out = output.getvalue() - if "nginx" in plugins: - # may be false while building distributions without plugins - self.assertTrue("--nginx-ctl" in out) - self.assertTrue("--manual-test-mode" not in out) - self.assertTrue("--checkpoints" not in out) - output.truncate(0) + def test_help(self): + self.assertRaises(SystemExit, self._call, ['--help']) + self.assertRaises(SystemExit, self._call, ['--help', 'all']) + plugins = disco.PluginsRegistry.find_all() + out = self._help_output(['--help', 'all']) + self.assertTrue("--configurator" in out) + self.assertTrue("how a cert is deployed" in out) + self.assertTrue("--manual-test-mode" in out) - self.assertRaises(SystemExit, self._call_stdout, ['-h']) - out = output.getvalue() - if "nginx" in plugins: - self.assertTrue("Use the Nginx plugin" in out) - else: - self.assertTrue("(nginx support is experimental" in out) - output.truncate(0) + out = self._help_output(['-h', 'nginx']) + if "nginx" in plugins: + # may be false while building distributions without plugins + self.assertTrue("--nginx-ctl" in out) + self.assertTrue("--manual-test-mode" not in out) + self.assertTrue("--checkpoints" not in out) - self.assertRaises(SystemExit, self._call_stdout, ['--help', 'plugins']) - out = output.getvalue() - self.assertTrue("--manual-test-mode" not in out) - self.assertTrue("--prepare" in out) - self.assertTrue("Plugin options" in out) - output.truncate(0) + out = self._help_output(['-h']) + if "nginx" in plugins: + self.assertTrue("Use the Nginx plugin" in out) + else: + self.assertTrue("(nginx support is experimental" in out) - self.assertRaises(SystemExit, self._call_stdout, ['--help', 'install']) - out = output.getvalue() - self.assertTrue("--cert-path" in out) - self.assertTrue("--key-path" in out) - output.truncate(0) + out = self._help_output(['--help', 'plugins']) + self.assertTrue("--manual-test-mode" not in out) + self.assertTrue("--prepare" in out) + self.assertTrue("Plugin options" in out) - self.assertRaises(SystemExit, self._call_stdout, ['--help', 'revoke']) - out = output.getvalue() - self.assertTrue("--cert-path" in out) - self.assertTrue("--key-path" in out) - output.truncate(0) + out = self._help_output(['--help', 'install']) + self.assertTrue("--cert-path" in out) + self.assertTrue("--key-path" in out) - self.assertRaises(SystemExit, self._call_stdout, ['--help', 'config_changes']) - out = output.getvalue() - self.assertTrue("--cert-path" not in out) - self.assertTrue("--key-path" not in out) - output.truncate(0) + out = self._help_output(['--help', 'revoke']) + self.assertTrue("--cert-path" in out) + self.assertTrue("--key-path" in out) - self.assertRaises(SystemExit, self._call_stdout, ['-h']) - out = output.getvalue() - from letsencrypt import cli - self.assertTrue(cli.usage_strings(plugins)[0] in out) + out = self._help_output(['-h', 'config_changes']) + self.assertTrue("--cert-path" not in out) + self.assertTrue("--key-path" not in out) + + out = self._help_output(['-h']) + from letsencrypt import cli + self.assertTrue(cli.usage_strings(plugins)[0] in out) def test_configurator_selection(self): real_plugins = disco.PluginsRegistry.find_all() From d5ccbdbcd29d9b79ba11d42f15c9661d7d60ab24 Mon Sep 17 00:00:00 2001 From: Dev & Sec Date: Fri, 30 Oct 2015 22:54:10 +0000 Subject: [PATCH 184/216] use `printf` instead of `echo -n` for better portability --- bootstrap/venv.sh | 6 +++--- letsencrypt-auto | 10 +++++----- letsencrypt/plugins/manual.py | 7 +++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/bootstrap/venv.sh b/bootstrap/venv.sh index ce31e6703..6ca082446 100755 --- a/bootstrap/venv.sh +++ b/bootstrap/venv.sh @@ -25,9 +25,9 @@ pip install -U letsencrypt letsencrypt-apache # letsencrypt-nginx echo echo "Congratulations, Let's Encrypt has been successfully installed/updated!" echo -echo -n "Your prompt should now be prepended with ($VENV_NAME). Next " -echo -n "time, if the prompt is different, 'source' this script again " -echo -n "before running 'letsencrypt'." +printf "%s" "Your prompt should now be prepended with ($VENV_NAME). Next " +printf "time, if the prompt is different, 'source' this script again " +printf "before running 'letsencrypt'." echo echo echo "You can now run 'letsencrypt --help'." diff --git a/letsencrypt-auto b/letsencrypt-auto index 769f5a1d9..41d2e4c97 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -70,7 +70,7 @@ then fi fi -echo -n "Updating letsencrypt and virtual environment dependencies..." +printf "Updating letsencrypt and virtual environment dependencies..." if [ "$VERBOSE" = 1 ] ; then echo $VENV_BIN/pip install -U setuptools @@ -83,15 +83,15 @@ if [ "$VERBOSE" = 1 ] ; then fi else $VENV_BIN/pip install -U setuptools > /dev/null - echo -n . + printf . $VENV_BIN/pip install -U pip > /dev/null - echo -n . + printf . # nginx is buggy / disabled for now... $VENV_BIN/pip install -U letsencrypt > /dev/null - echo -n . + printf . $VENV_BIN/pip install -U letsencrypt-apache > /dev/null if $VENV_BIN/pip freeze | grep -q letsencrypt-nginx ; then - echo -n . + printf . $VENV_BIN/pip install -U letsencrypt-nginx > /dev/null fi echo diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 866965200..9d1857edd 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -70,7 +70,7 @@ Are you OK with your IP being logged? CMD_TEMPLATE = """\ mkdir -p {root}/public_html/{response.URI_ROOT_PATH} cd {root}/public_html -echo -n {validation} > {response.URI_ROOT_PATH}/{encoded_token} +printf "%s" "{validation}" > {response.URI_ROOT_PATH}/{encoded_token} # run only once per server: $(command -v python2 || command -v python2.7 || command -v python2.6) -c \\ "import BaseHTTPServer, SimpleHTTPServer; \\ @@ -142,15 +142,14 @@ s.serve_forever()" """ ct=response.CONTENT_TYPE, port=port) if self.conf("test-mode"): logger.debug("Test mode. Executing the manual command: %s", command) - # sh shipped with OS X does't support echo -n - executable = "/bin/bash" if sys.platform == "darwin" else None + # sh shipped with OS X does't support echo -n, but supports printf try: self._httpd = subprocess.Popen( command, # don't care about setting stdout and stderr, # we're in test mode anyway shell=True, - executable=executable, + executable=None, # "preexec_fn" is UNIX specific, but so is "command" preexec_fn=os.setsid) except OSError as error: # ValueError should not happen! From 69365a7a0608731ad484fc1d91cbcc93935578ec Mon Sep 17 00:00:00 2001 From: Dev & Sec Date: Fri, 30 Oct 2015 23:27:16 +0000 Subject: [PATCH 185/216] fix drop quotes issue --- letsencrypt/plugins/manual.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index 9d1857edd..c76463f85 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -70,7 +70,7 @@ Are you OK with your IP being logged? CMD_TEMPLATE = """\ mkdir -p {root}/public_html/{response.URI_ROOT_PATH} cd {root}/public_html -printf "%s" "{validation}" > {response.URI_ROOT_PATH}/{encoded_token} +printf "%s" {validation} > {response.URI_ROOT_PATH}/{encoded_token} # run only once per server: $(command -v python2 || command -v python2.7 || command -v python2.6) -c \\ "import BaseHTTPServer, SimpleHTTPServer; \\ From 8558ad38605bdca702f5ea21d4e0073ccc6ef4ed Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 30 Oct 2015 16:53:04 -0700 Subject: [PATCH 186/216] Added comment about installer.save() --- letsencrypt/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index a3472cc59..6eb33f3fe 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -326,7 +326,7 @@ class Client(object): key_path=os.path.abspath(privkey_path), chain_path=chain_path, fullchain_path=fullchain_path) - self.installer.save() + self.installer.save() # needed by the Apache plugin self.installer.save("Deployed Let's Encrypt Certificate") # sites may have been enabled / final cleanup From f8185c1913610c4911d03e2512c1d0b7d59fb774 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 11:47:25 +0000 Subject: [PATCH 187/216] Add Python 2.6 setup.py classifiers. --- acme/setup.py | 1 + letsencrypt-apache/setup.py | 1 + letsencrypt-compatibility-test/setup.py | 1 + letsencrypt-nginx/setup.py | 1 + letshelp-letsencrypt/setup.py | 1 + 5 files changed, 5 insertions(+) diff --git a/acme/setup.py b/acme/setup.py index 2495a42ff..a6551a023 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -58,6 +58,7 @@ setup( 'License :: OSI Approved :: Apache Software License', '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', diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 2180219f1..e4dd11935 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -41,6 +41,7 @@ setup( 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 1608769e2..c791d51c4 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -38,6 +38,7 @@ setup( 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 5d80807d1..a669ad841 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -41,6 +41,7 @@ setup( 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index a63e0aad4..04b879e14 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -34,6 +34,7 @@ setup( 'Operating System :: POSIX :: Linux', 'Programming Language :: Python', 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Security', From d5a2c7fa18f8dce9f08e0bfe25fb7b06c74ac980 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 11:48:19 +0000 Subject: [PATCH 188/216] We don't use lsb-release any more --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 862e963b1..8586c3840 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ addons: mariadb: "10.0" apt: packages: # keep in sync with bootstrap/ubuntu.sh and Boulder - - lsb-release - python - python-dev - python-virtualenv From bd3d373d99fc1f8c58d5d0c0770d4b010cc125ba Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 11:48:33 +0000 Subject: [PATCH 189/216] Fix docs for deps.sh --- tools/deps.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/deps.sh b/tools/deps.sh index 28bfdaff5..6fb2bf63b 100755 --- a/tools/deps.sh +++ b/tools/deps.sh @@ -2,9 +2,9 @@ # # Find all Python imports. # -# ./deps.sh letsencrypt -# ./deps.sh acme -# ./deps.sh letsencrypt-apache +# ./tools/deps.sh letsencrypt +# ./tools/deps.sh acme +# ./tools/deps.sh letsencrypt-apache # ... # # Manually compare the output with deps in setup.py. From d019316ed37b2fd232e990f5ea0d7cdb69716bf7 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Sat, 31 Oct 2015 08:52:49 -0700 Subject: [PATCH 190/216] Documentation on our plans for ciphersuites --- docs/ciphers.rst | 197 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 docs/ciphers.rst diff --git a/docs/ciphers.rst b/docs/ciphers.rst new file mode 100644 index 000000000..ea4d328c3 --- /dev/null +++ b/docs/ciphers.rst @@ -0,0 +1,197 @@ +============ +Ciphersuites +============ + +.. contents:: Table of Contents + :local: + + +.. _ciphersuites: + +Introduction +============ + +Autoupdates +----------- + +Within certain limits, TLS server software can choose what kind of +cryptography to use when a client connects. These choices can affect +security, compatibility, and performance in complex ways. Most of +these options are independent of a particular certificate. The Let's +Encrypt client tries to provide defaults that we think are most useful +to our users. + +As described below, the Let's Encrypt client will default to modifying +server software's cryptographic settings to keep these up-to-date with +what we think are appropriate defaults when new versions of the Let's +Encrypt client are installed (for example, by an operating system package +manager). + +When this feature is implemented, this document will be updated +to describe how to disable these automatic changes. + + +Cryptographic choices +--------------------- + +Software that uses cryptography must inevitably make choices about what +kind of cryptography to use and how. These choices entail assumptions +about how well particular cryptographic mechanisms resist attack, and what +trade-offs are available and appropriate. The choices are constrained +by compatibility issues (in order to interoperate with other software, +an implementation must agree to use cryptographic mechanisms that the +other side also supports) and protocol issues (cryptographic mechanisms +must be specified in protocols and there must be a way to agree to use +them in a particular context). + +The best choices for a particular application may change over time in +response to new research, new standardization events, changes in computer +hardware, and changes in the prevalence of legacy software. Much important +research on cryptanalysis and cryptographic vulnerabilities is unpublished +because many researchers have been working in the interest of improving +some entities' communications security while weakening, or failing to +improve, others' security. But important information that improves our +understanding of the state of the art is published regularly. + +When enabling TLS support in a compatible web server (which is a separate +step from obtaining a certificate), Let's Encrypt has the ability to +update that web server's TLS configuration. Again, this is *different +from the cryptographic particulars of the certificate itself*; the +certificate as of the initial release will be RSA-signed using one of +Let's Encrypt's 2048-bit RSA keys, and will describe the subscriber's +RSA public key ("subject public key") of at least 2048 bits, which is +used for key establishment. + +Note that the subscriber's RSA public key can be used in a wide variety +of key establishment methods, most of which do not use RSA directly +for key exchange, but only for authenticating the server! For example, +in DHE and ECDHE key exchanges, the subject public key is just used to +sign other parameters for authentication. You do not have to "use RSA" +for other purposes just because you're using an RSA key for authentication. + +The certificate doesn't specify other cryptographic or ciphersuite +particulars; for example, it doesn't say whether or not parties should +use a particular symmetric algorithm like 3DES, or what cipher modes +they should use. All of these details are negotiated between client +and server independent of the content of the ciphersuite. The +Let's Encrypt project hopes to provide useful defaults that reflect +good security choices with respect to the publicly-known state of the +art. However, the Let's Encrypt certificate authority does *not* +dictate end-users' security policy, and any site is welcome to change +its preferences in accordance with its own policy or its administrators' +preferences, and use different cryptographic mechanisms or parameters, +or a different priority order, than the defaults provided by the Let's +Encrypt client. + +If you don't use the Let's Encrypt client to configure your server +directly, because the client doesn't integrate with your server software +or because you chose not to use this integration, then the cryptographic +defaults haven't been modified, and the cryptography chosen by the server +will still be whatever the default for your software was. For example, +if you obtain a certificate using *standalone* mode and then manually +install it in an IMAP or LDAP server, your cryptographic settings will +not be modified by the client in any way. + + +Sources of defaults +------------------- + +Initially, the Let's Encrypt client will configure users' servers to +use the cryptographic defaults recommended by the Mozilla project. +These settings are well-reasoned recommendations that carefully +consider client software compatibility. They are described at + +https://wiki.mozilla.org/Security/Server_Side_TLS + +and the version implemented by the Let's Encrypt client will be the +version that was most current as of the release date of each client +version. Mozilla offers three seperate sets of cryptographic options, +which trade off security and compatibility differently. These are +referred to as as the "Modern", "Intermediate", and "Old" configurations +(in order from most secure to least secure, and least-backwards compatible +to most-backwards compatible). The client will follow the Mozilla defaults +for the *Intermediate* configuration by default, at least with regards to +ciphersuites and TLS versions. Mozilla's web site describes which client +software will be compatible with each configuration. You can also use +the Qualys SSL Labs site, which the Let's Encrypt software will suggest +when installing a certificate, to test your server and see whether it +will be compatible with particular software versions. + +It will be possible to ask the Let's Encrypt client to instead apply +(and track) Modern or Old configurations. + +The Let's Encrypt project expects to follow the Mozilla recommendations +in the future as those recommendations are updated. (For example, some +users have proposed prioritizing a new ciphersuite known as ``0xcc13`` +which uses the ChaCha and Poly1305 algorithms, and which is already +implemented by the Chrome browser. Mozilla has delayed recommending +``0xcc13`` over compatibility and standardization concerns, but is likely +to recommend it in the future once these concerns have been addressed. At +that point, the Let's Encrypt client would likely follow the Mozilla +recommendations and favor the use of this ciphersuite as well.) + +The Let's Encrypt project may deviate from the Mozilla recommendations +in the future if good cause is shown and we believe our users' +priorities would be well-served by doing so. In general, please address +relevant proposals for changing priorities to the Mozilla security +team first, before asking the Let's Encrypt project to change the +client's priorities. The Mozilla security team is likely to have more +resources and expertise to bring to bear on evaluating reasons why its +recommendations should be updated. + +The Let's Encrpyt project will entertain proposals to create a *very* +small number of alternative configurations (apart from Modern, +Intermediate, and Old) that there's reason to believe would be widely +used by sysadmins; this would usually be a preferable course to modifying +an existing configuration. For example, if many sysadmins want their +servers configured to track a different expert recommendation, Let's +Encrypt could add an option to do so. + + +Resources for recommendations +----------------------------- + +In the course of considering how to handle this issue, we received +recommendations with sources of expert guidance on ciphersuites and other +cryptographic parameters. We're grateful to everyone who contributed +suggestions. The recommendations we received are available at + +https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance + +Let's Encrypt client users are welcome to review these authorities to +better inform their own cryptographic parameter choices. We also +welcome suggestions of other resources to add to this list. Please keep +in mind that different recommendations may reflect different priorities +or evaluations of trade-offs, especially related to compatibility! + + +Changing your settings +---------------------- + +This will probably look something like + +..code-block: shell + + letsencrypt --cipher-recommendations mozilla-secure + letsencrypt --cipher-recommendations mozilla-intermediate + letsencrypt --cipher-recommendations mozilla-old + +to track Mozilla's *Secure*, *Intermediate*, or *Old* recommendations, +and + +..code-block: shell + + letsencrypt --update-ciphers on + +to enable updating ciphers with each new Let's Encrypt client release, +or + +..code-block: shell + + letsencrypt --update-ciphers off + +to disable automatic configuration updates. These features have not yet +been implemented and this syntax may change then they are implemented. +The status of this feature is tracked as issue #1123 in our bug tracker. + +https://github.com/letsencrypt/letsencrypt/issues/1123 From f37aee39b2dc3219e33c0094aea3231cd6365bb4 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Sat, 31 Oct 2015 08:58:54 -0700 Subject: [PATCH 191/216] Mention that this hasn't been implemented at all --- docs/ciphers.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/ciphers.rst b/docs/ciphers.rst index ea4d328c3..12c403d09 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -192,6 +192,20 @@ or to disable automatic configuration updates. These features have not yet been implemented and this syntax may change then they are implemented. -The status of this feature is tracked as issue #1123 in our bug tracker. + + +TODO +---- + +The status of this feature is tracked as part of issue #1123 in our +bug tracker. https://github.com/letsencrypt/letsencrypt/issues/1123 + +Prior to implementation of #1123, the client does not actually modify +ciphersuites (this is intended to be implemented as a "configuration +enhancement", but the only configuration enhancement implemented +so far is redirecting HTTP requests to HTTPS in web servers, the +"redirect" enhancement). The changes here would probably be either a new +"ciphersuite" enhancement in each plugin that provides an installer, +or a family of enhancements, one per selectable ciphersuite configuration. From fb736135c50aa4660f4ba37ccd8a59ef3ec486d0 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 18:24:32 +0000 Subject: [PATCH 192/216] Fix manual plugin __doc__ indentantion --- letsencrypt/plugins/manual.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index c76463f85..18654c629 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -26,12 +26,13 @@ logger = logging.getLogger(__name__) class Authenticator(common.Plugin): """Manual Authenticator. - This plugin requires user's manual intervention in setting up a HTTP - server for solving SimpleHTTP challenges and thus does not need to be - run as a privileged process. Alternatively shows instructions on how - to use Python's built-in HTTP server. + This plugin requires user's manual intervention in setting up a HTTP + server for solving SimpleHTTP challenges and thus does not need to + be run as a privileged process. Alternatively shows instructions on + how to use Python's built-in HTTP server. .. todo:: Support for `~.challenges.DVSNI`. + """ zope.interface.implements(interfaces.IAuthenticator) zope.interface.classProvides(interfaces.IPluginFactory) From 3c2ea0c878c718948cf8f25ec6f3a47f808351ba Mon Sep 17 00:00:00 2001 From: Martin Brugger Date: Sat, 31 Oct 2015 20:09:04 +0100 Subject: [PATCH 193/216] Only works for me with port 80 and 443 forwarded For the docker client to work correctly I needed to forward both ports 443 and 80. I will take a closer look on how this is supposed to work. --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 73c3fe140..6d8130886 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -73,7 +73,7 @@ to, `install Docker`_, then issue the following command: .. code-block:: shell - sudo docker run -it --rm -p 443:443 --name letsencrypt \ + sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ quay.io/letsencrypt/letsencrypt:latest auth From dc81575527eb680d8101d94a0654c6ee30a211fa Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Mon, 19 Oct 2015 20:37:55 +0000 Subject: [PATCH 194/216] Factor out _TokenDVChallenge. --- acme/acme/challenges.py | 42 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 31095c04d..fd65b5e0f 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -76,26 +76,21 @@ class UnrecognizedChallenge(Challenge): return cls(jobj) -@Challenge.register -class SimpleHTTP(DVChallenge): - """ACME "simpleHttp" challenge. +class _TokenDVChallenge(DVChallenge): + """DV Challenge with token. :ivar unicode token: """ - typ = "simpleHttp" - TOKEN_SIZE = 128 / 8 # Based on the entropy value from the spec """Minimum size of the :attr:`token` in bytes.""" - URI_ROOT_PATH = ".well-known/acme-challenge" - """URI root path for the server provisioned resource.""" - # TODO: acme-spec doesn't specify token as base64-encoded value token = jose.Field( "token", encoder=jose.encode_b64jose, decoder=functools.partial( jose.decode_b64jose, size=TOKEN_SIZE, minimum=True)) + # XXX: rename to ~token_good_for_url @property def good_token(self): # XXX: @token.decoder """Is `token` good? @@ -109,6 +104,15 @@ class SimpleHTTP(DVChallenge): # URI_ROOT_PATH! return b'..' not in self.token and b'/' not in self.token + +@Challenge.register # pylint: disable=too-many-ancestors +class SimpleHTTP(_TokenDVChallenge): + """ACME "simpleHttp" challenge.""" + typ = "simpleHttp" + + URI_ROOT_PATH = ".well-known/acme-challenge" + """URI root path for the server provisioned resource.""" + @property def path(self): """Path (starting with '/') for provisioned resource.""" @@ -274,8 +278,8 @@ class SimpleHTTPProvisionedResource(jose.JSONObjectWithFields): tls = jose.Field("tls", omitempty=False) -@Challenge.register -class DVSNI(DVChallenge): +@Challenge.register # pylint: disable=too-many-ancestors +class DVSNI(_TokenDVChallenge): """ACME "dvsni" challenge. :ivar bytes token: Random data, **not** base64-encoded. @@ -286,13 +290,6 @@ class DVSNI(DVChallenge): PORT = 443 """Port to perform DVSNI challenge.""" - TOKEN_SIZE = 128 / 8 # Based on the entropy value from the spec - """Minimum size of the :attr:`token` in bytes.""" - - token = jose.Field( - "token", encoder=jose.encode_b64jose, decoder=functools.partial( - jose.decode_b64jose, size=TOKEN_SIZE, minimum=True)) - def gen_response(self, account_key, alg=jose.RS256, **kwargs): """Generate response. @@ -548,8 +545,8 @@ class ProofOfPossessionResponse(ChallengeResponse): return self.signature.verify(self.nonce) -@Challenge.register -class DNS(DVChallenge): +@Challenge.register # pylint: disable=too-many-ancestors +class DNS(_TokenDVChallenge): """ACME "dns" challenge. :ivar unicode token: @@ -560,13 +557,6 @@ class DNS(DVChallenge): LABEL = "_acme-challenge" """Label clients prepend to the domain name being validated.""" - TOKEN_SIZE = 128 / 8 # Based on the entropy value from the spec - """Minimum size of the :attr:`token` in bytes.""" - - token = jose.Field( - "token", encoder=jose.encode_b64jose, decoder=functools.partial( - jose.decode_b64jose, size=TOKEN_SIZE, minimum=True)) - def gen_validation(self, account_key, alg=jose.RS256, **kwargs): """Generate validation. From d2c5b87b953074255fda87a8d25bbde65803a5de Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 19:42:06 +0000 Subject: [PATCH 195/216] Fix documentation for account{,_public}_key docs in acme.challenges. account_key and account_public_key are JWK, not ComparableKey. --- acme/acme/challenges.py | 45 ++++++------------------------------ acme/acme/challenges_test.py | 33 +++++++++++--------------- acme/acme/jose/jws.py | 8 +++---- 3 files changed, 24 insertions(+), 62 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index fd65b5e0f..3ff16e1b3 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -189,13 +189,7 @@ class SimpleHTTPResponse(ChallengeResponse): :param .JWS validation: :param challenges.SimpleHTTP chall: - :type account_public_key: - `~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` - wrapped in `.ComparableKey` + :param .JWK account_public_key: :rtype: bool @@ -221,16 +215,9 @@ class SimpleHTTPResponse(ChallengeResponse): :param challenges.SimpleHTTP chall: Corresponding challenge. :param unicode domain: Domain name being verified. - :param account_public_key: Public key for the key pair + :param JWK account_public_key: Public key for the key pair being authorized. If ``None`` key verification is not performed! - :type account_public_key: - `~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` - wrapped in `.ComparableKey` :param int port: Port used in the validation. :returns: ``True`` iff validation is successful, ``False`` @@ -403,17 +390,12 @@ class DVSNIResponse(ChallengeResponse): :param .challenges.DVSNI chall: Corresponding challenge. :param str domain: Domain name being validated. - :type account_public_key: - `~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` - wrapped in `.ComparableKey` + :param JWK account_public_key: :param OpenSSL.crypto.X509 cert: Optional certificate. If not provided (``None``) certificate will be retrieved using `probe_cert`. + :returns: ``True`` iff client's control of the domain has been verified, ``False`` otherwise. :rtype: bool @@ -488,7 +470,7 @@ class ProofOfPossession(ContinuityChallenge): class Hints(jose.JSONObjectWithFields): """Hints for "proofOfPossession" challenge. - :ivar jwk: JSON Web Key (:class:`acme.jose.JWK`) + :ivar JWK jwk: JSON Web Key :ivar tuple cert_fingerprints: `tuple` of `unicode` :ivar tuple certs: Sequence of :class:`acme.jose.ComparableX509` certificates. @@ -575,14 +557,7 @@ class DNS(_TokenDVChallenge): """Check validation. :param JWS validation: - :type account_public_key: - `~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` - wrapped in `.ComparableKey` - + :param JWK account_public_key: :rtype: bool """ @@ -631,13 +606,7 @@ class DNSResponse(ChallengeResponse): """Check validation. :param challenges.DNS chall: - :type account_public_key: - `~cryptography.hazmat.primitives.asymmetric.rsa.RSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.dsa.DSAPublicKey` - or - `~cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey` - wrapped in `.ComparableKey` + :param JWK account_public_key: :rtype: bool diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index 2a12b4a64..4a8af2347 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -14,7 +14,7 @@ from acme import test_util CERT = test_util.load_cert('cert.pem') -KEY = test_util.load_rsa_private_key('rsa512_key.pem') +KEY = jose.JWKRSA(key=test_util.load_rsa_private_key('rsa512_key.pem')) class ChallengeTest(unittest.TestCase): @@ -237,18 +237,15 @@ class DVSNITest(unittest.TestCase): jose.DeserializationError, DVSNI.from_json, self.jmsg) def test_gen_response(self): - key = jose.JWKRSA(key=KEY) from acme.challenges import DVSNI self.assertEqual(self.msg, DVSNI.json_loads( - self.msg.gen_response(key).validation.payload.decode())) + self.msg.gen_response(KEY).validation.payload.decode())) class DVSNIResponseTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes def setUp(self): - self.key = jose.JWKRSA(key=KEY) - from acme.challenges import DVSNI self.chall = DVSNI( token=jose.b64decode(b'a82d5ff8ef740d12881f6d3c2277ab2e')) @@ -256,7 +253,7 @@ class DVSNIResponseTest(unittest.TestCase): from acme.challenges import DVSNIResponse self.validation = jose.JWS.sign( payload=self.chall.json_dumps(sort_keys=True).encode(), - key=self.key, alg=jose.RS256) + key=KEY, alg=jose.RS256) self.msg = DVSNIResponse(validation=self.validation) self.jmsg_to = { 'resource': 'challenge', @@ -340,22 +337,22 @@ class DVSNIResponseTest(unittest.TestCase): def test_simple_verify_wrong_payload(self): for payload in b'', b'{}': msg = self.msg.update(validation=jose.JWS.sign( - payload=payload, key=self.key, alg=jose.RS256)) + payload=payload, key=KEY, alg=jose.RS256)) self.assertFalse(msg.simple_verify( - self.chall, self.domain, self.key.public_key())) + self.chall, self.domain, KEY.public_key())) def test_simple_verify_wrong_token(self): msg = self.msg.update(validation=jose.JWS.sign( payload=self.chall.update(token=(b'b' * 20)).json_dumps().encode(), - key=self.key, alg=jose.RS256)) + key=KEY, alg=jose.RS256)) self.assertFalse(msg.simple_verify( - self.chall, self.domain, self.key.public_key())) + self.chall, self.domain, KEY.public_key())) @mock.patch('acme.challenges.DVSNIResponse.verify_cert', autospec=True) def test_simple_verify(self, mock_verify_cert): mock_verify_cert.return_value = mock.sentinel.verification self.assertEqual(mock.sentinel.verification, self.msg.simple_verify( - self.chall, self.domain, self.key.public_key(), + self.chall, self.domain, KEY.public_key(), cert=mock.sentinel.cert)) mock_verify_cert.assert_called_once_with(self.msg, mock.sentinel.cert) @@ -363,7 +360,7 @@ class DVSNIResponseTest(unittest.TestCase): def test_simple_verify_false_on_probe_error(self, mock_probe_cert): mock_probe_cert.side_effect = errors.Error self.assertFalse(self.msg.simple_verify( - self.chall, self.domain, self.key.public_key())) + self.chall, self.domain, KEY.public_key())) class RecoveryContactTest(unittest.TestCase): @@ -442,7 +439,7 @@ class RecoveryContactResponseTest(unittest.TestCase): class ProofOfPossessionHintsTest(unittest.TestCase): def setUp(self): - jwk = jose.JWKRSA(key=KEY.public_key()) + jwk = KEY.public_key() issuers = ( 'C=US, O=SuperT LLC, CN=SuperTrustworthy Public CA', 'O=LessTrustworthy CA Inc, CN=LessTrustworthy But StillSecure', @@ -511,7 +508,7 @@ class ProofOfPossessionTest(unittest.TestCase): def setUp(self): from acme.challenges import ProofOfPossession hints = ProofOfPossession.Hints( - jwk=jose.JWKRSA(key=KEY.public_key()), cert_fingerprints=(), + jwk=KEY.public_key(), cert_fingerprints=(), certs=(), serial_numbers=(), subject_key_identifiers=(), issuers=(), authorized_for=()) self.msg = ProofOfPossession( @@ -551,7 +548,7 @@ class ProofOfPossessionResponseTest(unittest.TestCase): # nonce and challenge nonce are the same, don't make the same # mistake here... signature = other.Signature( - alg=jose.RS256, jwk=jose.JWKRSA(key=KEY.public_key()), + alg=jose.RS256, jwk=KEY.public_key(), sig=b'\xa7\xc1\xe7\xe82o\xbc\xcd\xd0\x1e\x010#Z|\xaf\x15\x83' b'\x94\x8f#\x9b\nQo(\x80\x15,\x08\xfcz\x1d\xfd\xfd.\xaap' b'\xfa\x06\xd1\xa2f\x8d8X2>%d\xbd%\xe1T\xdd\xaa0\x18\xde' @@ -659,14 +656,12 @@ class DNSTest(unittest.TestCase): class DNSResponseTest(unittest.TestCase): def setUp(self): - self.key = jose.JWKRSA(key=KEY) - from acme.challenges import DNS self.chall = DNS(token=jose.b64decode( b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA")) self.validation = jose.JWS.sign( payload=self.chall.json_dumps(sort_keys=True).encode(), - key=self.key, alg=jose.RS256) + key=KEY, alg=jose.RS256) from acme.challenges import DNSResponse self.msg = DNSResponse(validation=self.validation) @@ -694,7 +689,7 @@ class DNSResponseTest(unittest.TestCase): def test_check_validation(self): self.assertTrue( - self.msg.check_validation(self.chall, self.key.public_key())) + self.msg.check_validation(self.chall, KEY.public_key())) if __name__ == '__main__': diff --git a/acme/acme/jose/jws.py b/acme/acme/jose/jws.py index 61a3b5aea..1a073e17d 100644 --- a/acme/acme/jose/jws.py +++ b/acme/acme/jose/jws.py @@ -104,7 +104,7 @@ class Header(json_util.JSONObjectWithFields): .. todo:: Supports only "jwk" header parameter lookup. :returns: (Public) key found in the header. - :rtype: :class:`acme.jose.jwk.JWK` + :rtype: .JWK :raises acme.jose.errors.Error: if key could not be found @@ -194,8 +194,7 @@ class Signature(json_util.JSONObjectWithFields): def verify(self, payload, key=None): """Verify. - :param key: Key used for verification. - :type key: :class:`acme.jose.jwk.JWK` + :param JWK key: Key used for verification. """ key = self.combined.find_key() if key is None else key @@ -208,8 +207,7 @@ class Signature(json_util.JSONObjectWithFields): protect=frozenset(), **kwargs): """Sign. - :param key: Key for signature. - :type key: :class:`acme.jose.jwk.JWK` + :param JWK key: Key for signature. """ assert isinstance(key, alg.kty) From 2a4ebb90ef5cbdc7eaa291a21eb784f87ce48124 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 20:31:32 +0000 Subject: [PATCH 196/216] Contet-Type snippets for webroot --- letsencrypt/plugins/webroot.py | 41 +++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index f11325f57..f58c33970 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -1,4 +1,43 @@ -"""Webroot plugin.""" +"""Webroot plugin. + +Content-Type +------------ + +This plugin requires your webserver to use a specific `Content-Type` +header in the HTTP response. + +Apache2 +~~~~~~~ + +.. note:: Instructions written and tested for Debian Jessie. Other + operating systems might use something very similar, but you might + still need to readjust some commands. + +Create ``/etc/apache2/conf-available/letsencrypt-simplehttp.conf``, with +the following contents:: + + + + Header set Content-Type "application/jose+json" + + + +and then run ``a2enmod headers; a2enconf letsencrypt``; depending on the +output you will have to either ``service apache2 restart`` or ``service +apache2 reload``. + +nginx +~~~~~ + +Use the following snippet in your ``server{...}`` stanza:: + + location ~ /.well-known/acme-challenge/(.*) { + default_type application/jose+json; + } + +and reload your daemon. + +""" import errno import logging import os From c529f742f28adeec07522d5aaba5a8643ce4b7ab Mon Sep 17 00:00:00 2001 From: poly Date: Sun, 1 Nov 2015 08:46:34 +0400 Subject: [PATCH 197/216] added email reminder documentation --- docs/using.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/using.rst b/docs/using.rst index 6d8130886..78e4c6d37 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -163,6 +163,9 @@ sure that UI doesn't prompt for any details you can add the command to ``crontab`` (make it less than every 90 days to avoid problems, say every month). +Please note that the CA will send notification emails to the address +you provide if you do not renew certificates that are about to expire. + Let's Encrypt is working hard on automating the renewal process. Until the tool is ready, we are sorry for the inconvenience! From 602f0b2dbefd2321e0f7a7da523569d826b2d354 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 20:41:15 +0000 Subject: [PATCH 198/216] Add http-01 to acme --- acme/acme/challenges.py | 223 +++++++++++++++++++++++++++++++++-- acme/acme/challenges_test.py | 147 +++++++++++++++++++++++ acme/acme/jose/jwk.py | 2 + acme/acme/messages_test.py | 2 +- docs/contributing.rst | 2 +- 5 files changed, 367 insertions(+), 9 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 3ff16e1b3..750f2be8d 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -1,9 +1,11 @@ """ACME Identifier Validation Challenges.""" +import abc import functools import hashlib import logging import socket +from cryptography.hazmat.primitives import hashes import OpenSSL import requests @@ -79,7 +81,7 @@ class UnrecognizedChallenge(Challenge): class _TokenDVChallenge(DVChallenge): """DV Challenge with token. - :ivar unicode token: + :ivar bytes token: """ TOKEN_SIZE = 128 / 8 # Based on the entropy value from the spec @@ -105,6 +107,217 @@ class _TokenDVChallenge(DVChallenge): return b'..' not in self.token and b'/' not in self.token +class KeyAuthorizationChallengeResponse(ChallengeResponse): + """Response to Challenges based on Key Authorization. + + :param unicode key_authorization: + + """ + key_authorization = jose.Field("keyAuthorization") + thumbprint_hash_function = hashes.SHA256 + + def verify(self, chall, account_public_key): + """Verify the key authorization. + + :param KeyAuthorization chall: Challenge that corresponds to + this response. + :param JWK account_public_key: + + :return: ``True`` iff verification of the key authorization was + successful. + :rtype: bool + + """ + parts = self.key_authorization.split('.') # pylint: disable=no-member + if len(parts) != 2: + logger.debug("Key authorization (%r) is not well formed", + self.key_authorization) + return False + + if parts[0] != chall.encode("token"): + logger.debug("Mismatching token in key authorization: " + "%r instead of %r", parts[0], chall.encode("token")) + return False + + thumbprint = jose.b64encode(account_public_key.thumbprint( + hash_function=self.thumbprint_hash_function)).decode() + if parts[1] != thumbprint: + logger.debug("Mismatching thumbprint in key authorization: " + "%r instead of %r", parts[0], thumbprint) + return False + + return True + + +class KeyAuthorizationChallenge(_TokenDVChallenge): + # pylint: disable=abstract-class-little-used,too-many-ancestors + """Challenge based on Key Authorization. + + :param response_cls: Subclass of `KeyAuthorizationChallengeResponse` + that will be used to generate `response`. + + """ + __metaclass__ = abc.ABCMeta + + response_cls = NotImplemented + thumbprint_hash_function = ( + KeyAuthorizationChallengeResponse.thumbprint_hash_function) + + def key_authorization(self, account_key): + """Generate Key Authorization. + + :param JWK account_key: + :rtype unicode: + + """ + return self.encode("token") + "." + jose.b64encode( + account_key.thumbprint( + hash_function=self.thumbprint_hash_function)).decode() + + def response(self, account_key): + """Generate response to the challenge. + + :param JWK account_key: + + :returns: Response (initialized `response_cls`) to the challenge. + :rtype: KeyAuthorizationChallengeResponse + + """ + return self.response_cls( + key_authorization=self.key_authorization(account_key)) + + @abc.abstractmethod + def validation(self, account_key): + """Generate validation for the challenge. + + Subclasses must implement this method, but they are likely to + return completely different data structures, depending on what's + necessary to complete the challenge. Interepretation of that + return value must be known to the caller. + + :param JWK account_key: + :returns: Challenge-specific validation. + + """ + raise NotImplementedError() # pragma: no cover + + def response_and_validation(self, account_key): + """Generate response and validation. + + Convenience function that return results of `response` and + `validation`. + + :param JWK account_key: + :rtype: tuple + + """ + return (self.response(account_key), self.validation(account_key)) + + +@ChallengeResponse.register +class HTTP01Response(KeyAuthorizationChallengeResponse): + """ACME http-01 challenge response.""" + typ = "http-01" + + PORT = 80 + + def simple_verify(self, chall, domain, account_public_key, port=None): + """Simple verify. + + :param challenges.SimpleHTTP chall: Corresponding challenge. + :param unicode domain: Domain name being verified. + :param account_public_key: Public key for the key pair + being authorized. If ``None`` key verification is not + performed! + :param JWK account_public_key: + :param int port: Port used in the validation. + + :returns: ``True`` iff validation is successful, ``False`` + otherwise. + :rtype: bool + + """ + if not self.verify(chall, account_public_key): + logger.debug("Verification of key authorization in response failed") + return False + + # TODO: ACME specification defines URI template that doesn't + # allow to use a custom port... Make sure port is not in the + # request URI, if it's standard. + if port is not None and port != self.PORT: + logger.warning( + "Using non-standard port for SimpleHTTP verification: %s", port) + domain += ":{0}".format(port) + + uri = self.uri(domain, chall) + logger.debug("Verifying %s at %s...", chall.typ, uri) + try: + http_response = requests.get(uri) + except requests.exceptions.RequestException as error: + logger.error("Unable to reach %s: %s", uri, error) + return False + logger.debug("Received %s: %s. Headers: %s", http_response, + http_response.text, http_response.headers) + + found_ct = http_response.headers.get( + "Content-Type", chall.CONTENT_TYPE) + if found_ct != chall.CONTENT_TYPE: + logger.debug("Wrong Content-Type: found %r, expected %r", + found_ct, chall.CONTENT_TYPE) + return False + + if self.key_authorization != http_response.text: + logger.debug("Key authorization from response (%r) doesn't match " + "HTTP response (%r)", self.key_authorization, + http_response.text) + return False + + return True + + +@Challenge.register # pylint: disable=too-many-ancestors +class HTTP01(KeyAuthorizationChallenge): + """ACME http-01 challenge.""" + response_cls = HTTP01Response + typ = response_cls.typ + + CONTENT_TYPE = "text/plain" + """Content-Type header that must be used for provisioned resource.""" + + URI_ROOT_PATH = ".well-known/acme-challenge" + """URI root path for the server provisioned resource.""" + + @property + def path(self): + """Path (starting with '/') for provisioned resource. + + :rtype: string + + """ + return '/' + self.URI_ROOT_PATH + '/' + self.encode('token') + + def uri(self, domain): + """Create an URI to the provisioned resource. + + Forms an URI to the HTTPS server provisioned resource + (containing :attr:`~SimpleHTTP.token`). + + :param unicode domain: Domain name being verified. + :rtype: string + + """ + return "http://" + domain + self.path + + def validation(self, account_key): + """Generate validation. + + :param JWK account_key: + :rtype: unicode + + """ + return self.key_authorization(account_key) + + @Challenge.register # pylint: disable=too-many-ancestors class SimpleHTTP(_TokenDVChallenge): """ACME "simpleHttp" challenge.""" @@ -229,7 +442,7 @@ class SimpleHTTPResponse(ChallengeResponse): # allow to use a custom port... Make sure port is not in the # request URI, if it's standard. if port is not None and port != self.port: - logger.warn( + logger.warning( "Using non-standard port for SimpleHTTP verification: %s", port) domain += ":{0}".format(port) @@ -529,11 +742,7 @@ class ProofOfPossessionResponse(ChallengeResponse): @Challenge.register # pylint: disable=too-many-ancestors class DNS(_TokenDVChallenge): - """ACME "dns" challenge. - - :ivar unicode token: - - """ + """ACME "dns" challenge.""" typ = "dns" LABEL = "_acme-challenge" diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index 4a8af2347..53e6b528a 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -43,6 +43,149 @@ class UnrecognizedChallengeTest(unittest.TestCase): self.chall, UnrecognizedChallenge.from_json(self.jobj)) +class KeyAuthorizationChallengeResponseTest(unittest.TestCase): + + def setUp(self): + def _encode(name): + assert name == "token" + return "foo" + self.chall = mock.Mock() + self.chall.encode.side_effect = _encode + + def test_verify_ok(self): + from acme.challenges import KeyAuthorizationChallengeResponse + response = KeyAuthorizationChallengeResponse( + key_authorization='foo.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY') + self.assertTrue(response.verify(self.chall, KEY.public_key())) + + def test_verify_wrong_token(self): + from acme.challenges import KeyAuthorizationChallengeResponse + response = KeyAuthorizationChallengeResponse( + key_authorization='bar.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY') + self.assertFalse(response.verify(self.chall, KEY.public_key())) + + def test_verify_wrong_thumbprint(self): + from acme.challenges import KeyAuthorizationChallengeResponse + response = KeyAuthorizationChallengeResponse( + key_authorization='foo.oKGqedy-b-acd5eoybm2f-NVFxv') + self.assertFalse(response.verify(self.chall, KEY.public_key())) + + def test_verify_wrong_form(self): + from acme.challenges import KeyAuthorizationChallengeResponse + response = KeyAuthorizationChallengeResponse( + key_authorization='.foo.oKGqedy-b-acd5eoybm2f-NVFxvyOoET5CNy3xnv8WY') + self.assertFalse(response.verify(self.chall, KEY.public_key())) + + +class HTTP01ResponseTest(unittest.TestCase): + # pylint: disable=too-many-instance-attributes + + def setUp(self): + from acme.challenges import HTTP01Response + self.msg = HTTP01Response(key_authorization=u'foo') + self.jmsg = { + 'resource': 'challenge', + 'type': 'http-01', + 'keyAuthorization': u'foo', + } + + from acme.challenges import HTTP01 + self.chall = HTTP01(token=(b'x' * 16)) + self.response = self.chall.response(KEY) + self.good_headers = {'Content-Type': HTTP01.CONTENT_TYPE} + + def test_to_partial_json(self): + self.assertEqual(self.jmsg, self.msg.to_partial_json()) + + def test_from_json(self): + from acme.challenges import HTTP01Response + self.assertEqual( + self.msg, HTTP01Response.from_json(self.jmsg)) + + def test_from_json_hashable(self): + from acme.challenges import HTTP01Response + hash(HTTP01Response.from_json(self.jmsg)) + + def test_simple_verify_bad_key_authorization(self): + key2 = jose.JWKRSA.load(test_util.load_vector('rsa256_key.pem')) + self.response.simple_verify(self.chall, "local", key2.public_key()) + + @mock.patch("acme.challenges.requests.get") + def test_simple_verify_good_validation(self, mock_get): + validation = self.chall.validation(KEY) + mock_get.return_value = mock.MagicMock( + text=validation, headers=self.good_headers) + self.assertTrue(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + mock_get.assert_called_once_with(self.chall.uri("local")) + + @mock.patch("acme.challenges.requests.get") + def test_simple_verify_bad_validation(self, mock_get): + mock_get.return_value = mock.MagicMock( + text="!", headers=self.good_headers) + self.assertFalse(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + + @mock.patch("acme.challenges.requests.get") + def test_simple_verify_bad_content_type(self, mock_get): + mock_get().text = self.chall.token + self.assertFalse(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + + @mock.patch("acme.challenges.requests.get") + def test_simple_verify_connection_error(self, mock_get): + mock_get.side_effect = requests.exceptions.RequestException + self.assertFalse(self.response.simple_verify( + self.chall, "local", KEY.public_key())) + + @mock.patch("acme.challenges.requests.get") + def test_simple_verify_port(self, mock_get): + self.response.simple_verify( + self.chall, domain="local", + account_public_key=KEY.public_key(), port=8080) + self.assertEqual("local:8080", urllib_parse.urlparse( + mock_get.mock_calls[0][1][0]).netloc) + + +class HTTP01Test(unittest.TestCase): + + def setUp(self): + from acme.challenges import HTTP01 + self.msg = HTTP01( + token=jose.decode_b64jose( + 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA')) + self.jmsg = { + 'type': 'http-01', + 'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA', + } + + def test_path(self): + self.assertEqual(self.msg.path, '/.well-known/acme-challenge/' + 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA') + + def test_uri(self): + self.assertEqual( + 'http://example.com/.well-known/acme-challenge/' + 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA', + self.msg.uri('example.com')) + + def test_to_partial_json(self): + self.assertEqual(self.jmsg, self.msg.to_partial_json()) + + def test_from_json(self): + from acme.challenges import HTTP01 + self.assertEqual(self.msg, HTTP01.from_json(self.jmsg)) + + def test_from_json_hashable(self): + from acme.challenges import HTTP01 + hash(HTTP01.from_json(self.jmsg)) + + def test_good_token(self): + self.assertTrue(self.msg.good_token) + self.assertFalse( + self.msg.update(token=b'..').good_token) + + class SimpleHTTPTest(unittest.TestCase): def setUp(self): @@ -55,6 +198,10 @@ class SimpleHTTPTest(unittest.TestCase): 'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA', } + def test_path(self): + self.assertEqual(self.msg.path, '/.well-known/acme-challenge/' + 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA') + def test_to_partial_json(self): self.assertEqual(self.jmsg, self.msg.to_partial_json()) diff --git a/acme/acme/jose/jwk.py b/acme/acme/jose/jwk.py index da61b0c4e..4d07229b3 100644 --- a/acme/acme/jose/jwk.py +++ b/acme/acme/jose/jwk.py @@ -47,6 +47,8 @@ class JWK(json_util.TypedJSONObjectWithFields): https://tools.ietf.org/html/rfc7638 + :returns bytes: + """ digest = hashes.Hash(hash_function(), backend=default_backend()) digest.update(json.dumps( diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index d2d859bc5..6c1c4f596 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -278,7 +278,7 @@ class AuthorizationTest(unittest.TestCase): self.challbs = ( ChallengeBody( uri='http://challb1', status=STATUS_VALID, - chall=challenges.SimpleHTTP(token=b'IlirfxKKXAsHtmzK29Pj8A')), + chall=challenges.HTTP01(token=b'IlirfxKKXAsHtmzK29Pj8A')), ChallengeBody(uri='http://challb2', status=STATUS_VALID, chall=challenges.DNS( token=b'DGyRejmCefe7v4NfDGDKfA')), diff --git a/docs/contributing.rst b/docs/contributing.rst index f82d7a583..a3baa7bc5 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -152,7 +152,7 @@ the ACME server. From the protocol, there are essentially two different types of challenges. Challenges that must be solved by individual plugins in order to satisfy domain validation (subclasses of `~.DVChallenge`, i.e. `~.challenges.DVSNI`, -`~.challenges.SimpleHTTPS`, `~.challenges.DNS`) and continuity specific +`~.challenges.HTTP01`, `~.challenges.DNS`) and continuity specific challenges (subclasses of `~.ContinuityChallenge`, i.e. `~.challenges.RecoveryToken`, `~.challenges.RecoveryContact`, `~.challenges.ProofOfPossession`). Continuity challenges are From 01bc073111d139183e24c9d702d5942beb864022 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 1 Nov 2015 10:54:01 +0000 Subject: [PATCH 199/216] Quickfix for misterious abstract-class-little-used --- acme/acme/jose/jwk.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/acme/acme/jose/jwk.py b/acme/acme/jose/jwk.py index 4d07229b3..c82134da9 100644 --- a/acme/acme/jose/jwk.py +++ b/acme/acme/jose/jwk.py @@ -21,6 +21,12 @@ from acme.jose import util logger = logging.getLogger(__name__) +# TODO: bug in pylint? +# ************* Module acme.challenges +# R:153, 0: Abstract class is only referenced 1 times (abstract-class-little-used) +# pylint: disable=abstract-class-little-used + + class JWK(json_util.TypedJSONObjectWithFields): # pylint: disable=too-few-public-methods """JSON Web Key.""" From bb0843b6c6662fd75a78523151ae3543e17e070e Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 21:35:12 +0000 Subject: [PATCH 200/216] acme_util.HTTP01* --- letsencrypt/tests/acme_util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/letsencrypt/tests/acme_util.py b/letsencrypt/tests/acme_util.py index 235810435..1e3086b18 100644 --- a/letsencrypt/tests/acme_util.py +++ b/letsencrypt/tests/acme_util.py @@ -14,6 +14,8 @@ KEY = test_util.load_rsa_private_key('rsa512_key.pem') # Challenges SIMPLE_HTTP = challenges.SimpleHTTP( token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA") +HTTP01 = challenges.HTTP01( + token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA") DVSNI = challenges.DVSNI( token=jose.b64decode(b"evaGxfADs6pSRb2LAv9IZf17Dt3juxGJyPCt92wrDoA")) DNS = challenges.DNS(token="17817c66b60ce2e4012dfad92657527a") @@ -81,6 +83,7 @@ def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name # Pending ChallengeBody objects DVSNI_P = chall_to_challb(DVSNI, messages.STATUS_PENDING) SIMPLE_HTTP_P = chall_to_challb(SIMPLE_HTTP, messages.STATUS_PENDING) +HTTP01_P = chall_to_challb(HTTP01, messages.STATUS_PENDING) DNS_P = chall_to_challb(DNS, messages.STATUS_PENDING) RECOVERY_CONTACT_P = chall_to_challb(RECOVERY_CONTACT, messages.STATUS_PENDING) POP_P = chall_to_challb(POP, messages.STATUS_PENDING) From 5ef2507b0c33031e5df25f7cd3b966b325475860 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 20:42:27 +0000 Subject: [PATCH 201/216] KeyAuthorizationAnnotatedChallenge --- letsencrypt/achallenges.py | 9 +++++++++ letsencrypt/auth_handler.py | 4 +++- letsencrypt/tests/auth_handler_test.py | 19 +++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/letsencrypt/achallenges.py b/letsencrypt/achallenges.py index e86f51a3f..7aa2bd35a 100644 --- a/letsencrypt/achallenges.py +++ b/letsencrypt/achallenges.py @@ -45,6 +45,15 @@ class AnnotatedChallenge(jose.ImmutableMap): return getattr(self.challb, name) +class KeyAuthorizationAnnotatedChallenge(AnnotatedChallenge): + """Client annotated `KeyAuthorizationChallenge` challenge.""" + __slots__ = ('challb', 'domain', 'account_key') + + def response_and_validation(self): + """Generate response and validation.""" + return self.challb.chall.response_and_validation(self.account_key) + + class DVSNI(AnnotatedChallenge): """Client annotated "dvsni" ACME challenge. diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 4f2a6fa3e..f7b6bd507 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -358,7 +358,9 @@ def challb_to_achall(challb, account_key, domain): elif isinstance(chall, challenges.ProofOfPossession): return achallenges.ProofOfPossession( challb=challb, domain=domain) - + elif isinstance(chall, challenges.KeyAuthorizationChallenge): + return achallenges.KeyAuthorizationAnnotatedChallenge( + challb=challb, domain=domain, account_key=account_key) else: raise errors.Error( "Received unsupported challenge of type: %s", chall.typ) diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 18ee56081..247fca4e1 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -9,6 +9,7 @@ from acme import challenges from acme import client as acme_client from acme import messages +from letsencrypt import achallenges from letsencrypt import errors from letsencrypt import le_util @@ -283,6 +284,22 @@ class PollChallengesTest(unittest.TestCase): return (new_authzr, "response") +class ChallbToAchallTest(unittest.TestCase): + """Tests for letsencrypt.auth_handler.challb_to_achall.""" + + def _call(self, challb): + from letsencrypt.auth_handler import challb_to_achall + return challb_to_achall(challb, "account_key", "domain") + + def test_it(self): + self.assertEqual( + self._call(acme_util.HTTP01_P), + achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, account_key="account_key", + domain="domain"), + ) + + class GenChallengePathTest(unittest.TestCase): """Tests for letsencrypt.auth_handler.gen_challenge_path. @@ -425,8 +442,6 @@ class ReportFailedChallsTest(unittest.TestCase): # pylint: disable=protected-access def setUp(self): - from letsencrypt import achallenges - kwargs = { "chall": acme_util.SIMPLE_HTTP, "uri": "uri", From fd209eab66f8818796c6be5fed2bc2ace9776d7e Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 20:42:44 +0000 Subject: [PATCH 202/216] http-01 for manual plugin --- letsencrypt/plugins/manual.py | 24 ++++++++++++------------ letsencrypt/plugins/manual_test.py | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index c76463f85..ffaad3d99 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -27,7 +27,7 @@ class Authenticator(common.Plugin): """Manual Authenticator. This plugin requires user's manual intervention in setting up a HTTP - server for solving SimpleHTTP challenges and thus does not need to be + server for solving http-01 challenges and thus does not need to be run as a privileged process. Alternatively shows instructions on how to use Python's built-in HTTP server. @@ -68,9 +68,9 @@ Are you OK with your IP being logged? # anything recursively under the cwd CMD_TEMPLATE = """\ -mkdir -p {root}/public_html/{response.URI_ROOT_PATH} +mkdir -p {root}/public_html/{achall.URI_ROOT_PATH} cd {root}/public_html -printf "%s" {validation} > {response.URI_ROOT_PATH}/{encoded_token} +printf "%s" {validation} > {achall.URI_ROOT_PATH}/{encoded_token} # run only once per server: $(command -v python2 || command -v python2.7 || command -v python2.6) -c \\ "import BaseHTTPServer, SimpleHTTPServer; \\ @@ -95,14 +95,14 @@ s.serve_forever()" """ def more_info(self): # pylint: disable=missing-docstring,no-self-use return ("This plugin requires user's manual intervention in setting " - "up an HTTP server for solving SimpleHTTP challenges and thus " + "up an HTTP server for solving http-01 challenges and thus " "does not need to be run as a privileged process. " "Alternatively shows instructions on how to use Python's " "built-in HTTP server.") def get_chall_pref(self, domain): # pylint: disable=missing-docstring,no-self-use,unused-argument - return [challenges.SimpleHTTP] + return [challenges.HTTP01] def perform(self, achalls): # pylint: disable=missing-docstring responses = [] @@ -130,16 +130,16 @@ s.serve_forever()" """ # same path for each challenge response would be easier for # users, but will not work if multiple domains point at the # same server: default command doesn't support virtual hosts - response, validation = achall.gen_response_and_validation( - tls=False) # SimpleHTTP TLS is dead: ietf-wg-acme/acme#7 + response, validation = achall.response_and_validation() port = (response.port if self.config.simple_http_port is None else int(self.config.simple_http_port)) command = self.CMD_TEMPLATE.format( root=self._root, achall=achall, response=response, - validation=pipes.quote(validation.json_dumps()), + # TODO(kuba): pipes still necessary? + validation=pipes.quote(validation), encoded_token=achall.chall.encode("token"), - ct=response.CONTENT_TYPE, port=port) + ct=achall.CONTENT_TYPE, port=port) if self.conf("test-mode"): logger.debug("Test mode. Executing the manual command: %s", command) # sh shipped with OS X does't support echo -n, but supports printf @@ -168,9 +168,9 @@ s.serve_forever()" """ raise errors.PluginError("Must agree to IP logging to proceed") self._notify_and_wait(self.MESSAGE_TEMPLATE.format( - validation=validation.json_dumps(), response=response, - uri=response.uri(achall.domain, achall.challb.chall), - ct=response.CONTENT_TYPE, command=command)) + validation=validation, response=response, + uri=achall.chall.uri(achall.domain), + ct=achall.CONTENT_TYPE, command=command)) if response.simple_verify( achall.chall, achall.domain, diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index a52129635..431cd07a7 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -25,8 +25,8 @@ class AuthenticatorTest(unittest.TestCase): self.config = mock.MagicMock( simple_http_port=8080, manual_test_mode=False) self.auth = Authenticator(config=self.config, name="manual") - self.achalls = [achallenges.SimpleHTTP( - challb=acme_util.SIMPLE_HTTP_P, domain="foo.com", account_key=KEY)] + self.achalls = [achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY)] config_test_mode = mock.MagicMock( simple_http_port=8080, manual_test_mode=True) @@ -45,13 +45,13 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") @mock.patch("letsencrypt.plugins.manual.sys.stdout") - @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") + @mock.patch("acme.challenges.HTTP01Response.simple_verify") @mock.patch("__builtin__.raw_input") def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): mock_verify.return_value = True mock_interaction().yesno.return_value = True - resp = challenges.SimpleHTTPResponse(tls=False) + resp = self.achalls[0].response(KEY) self.assertEqual([resp], self.auth.perform(self.achalls)) self.assertEqual(1, mock_raw_input.call_count) mock_verify.assert_called_with( @@ -89,7 +89,7 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("letsencrypt.plugins.manual.socket.socket") @mock.patch("letsencrypt.plugins.manual.time.sleep", autospec=True) - @mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify", + @mock.patch("acme.challenges.HTTP01Response.simple_verify", autospec=True) @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) def test_perform_test_mode(self, mock_popen, mock_verify, mock_sleep, From ea3611afe6b7e5e605dff6bcd31eb1dbdf235486 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 28 Oct 2015 21:27:04 +0000 Subject: [PATCH 203/216] http-01 for standalone --- acme/acme/challenges.py | 2 +- acme/acme/standalone.py | 26 +++++++++--------- acme/acme/standalone_test.py | 29 ++++++++++---------- docs/using.rst | 2 +- letsencrypt/plugins/standalone.py | 36 ++++++++++++------------- letsencrypt/plugins/standalone_test.py | 37 +++++++++++++------------- tests/boulder-integration.sh | 2 +- 7 files changed, 66 insertions(+), 68 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 750f2be8d..86ff0a902 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -249,7 +249,7 @@ class HTTP01Response(KeyAuthorizationChallengeResponse): "Using non-standard port for SimpleHTTP verification: %s", port) domain += ":{0}".format(port) - uri = self.uri(domain, chall) + uri = chall.uri(domain) logger.debug("Verifying %s at %s...", chall.typ, uri) try: http_response = requests.get(uri) diff --git a/acme/acme/standalone.py b/acme/acme/standalone.py index 310e61995..1466671e3 100644 --- a/acme/acme/standalone.py +++ b/acme/acme/standalone.py @@ -58,26 +58,26 @@ class DVSNIServer(TLSServer, ACMEServerMixin): self, server_address, socketserver.BaseRequestHandler, certs=certs) -class SimpleHTTPServer(BaseHTTPServer.HTTPServer, ACMEServerMixin): - """SimpleHTTP Server.""" +class HTTP01Server(BaseHTTPServer.HTTPServer, ACMEServerMixin): + """HTTP01 Server.""" def __init__(self, server_address, resources): BaseHTTPServer.HTTPServer.__init__( - self, server_address, SimpleHTTPRequestHandler.partial_init( + self, server_address, HTTP01RequestHandler.partial_init( simple_http_resources=resources)) -class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): - """SimpleHTTP challenge handler. +class HTTP01RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): + """HTTP01 challenge handler. Adheres to the stdlib's `socketserver.BaseRequestHandler` interface. - :ivar set simple_http_resources: A set of `SimpleHTTPResource` + :ivar set simple_http_resources: A set of `HTTP01Resource` objects. TODO: better name? """ - SimpleHTTPResource = collections.namedtuple( - "SimpleHTTPResource", "chall response validation") + HTTP01Resource = collections.namedtuple( + "HTTP01Resource", "chall response validation") def __init__(self, *args, **kwargs): self.simple_http_resources = kwargs.pop("simple_http_resources", set()) @@ -86,7 +86,7 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): # pylint: disable=invalid-name,missing-docstring if self.path == "/": self.handle_index() - elif self.path.startswith("/" + challenges.SimpleHTTP.URI_ROOT_PATH): + elif self.path.startswith("/" + challenges.HTTP01.URI_ROOT_PATH): self.handle_simple_http_resource() else: self.handle_404() @@ -106,15 +106,15 @@ class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self.wfile.write(b"404") def handle_simple_http_resource(self): - """Handle SimpleHTTP provisioned resources.""" + """Handle HTTP01 provisioned resources.""" for resource in self.simple_http_resources: if resource.chall.path == self.path: - logger.debug("Serving SimpleHTTP with token %r", + logger.debug("Serving HTTP01 with token %r", resource.chall.encode("token")) self.send_response(http_client.OK) - self.send_header("Content-type", resource.response.CONTENT_TYPE) + self.send_header("Content-type", resource.chall.CONTENT_TYPE) self.end_headers() - self.wfile.write(resource.validation.json_dumps().encode()) + self.wfile.write(resource.validation.encode()) return else: # pylint: disable=useless-else-on-loop logger.debug("No resources to serve") diff --git a/acme/acme/standalone_test.py b/acme/acme/standalone_test.py index ed015b826..85ef6ab14 100644 --- a/acme/acme/standalone_test.py +++ b/acme/acme/standalone_test.py @@ -54,16 +54,16 @@ class DVSNIServerTest(unittest.TestCase): jose.ComparableX509(self.certs[b'localhost'][1])) -class SimpleHTTPServerTest(unittest.TestCase): - """Tests for acme.standalone.SimpleHTTPServer.""" +class HTTP01ServerTest(unittest.TestCase): + """Tests for acme.standalone.HTTP01Server.""" def setUp(self): self.account_key = jose.JWK.load( test_util.load_vector('rsa1024_key.pem')) self.resources = set() - from acme.standalone import SimpleHTTPServer - self.server = SimpleHTTPServer(('', 0), resources=self.resources) + from acme.standalone import HTTP01Server + self.server = HTTP01Server(('', 0), resources=self.resources) # pylint: disable=no-member self.port = self.server.socket.getsockname()[1] @@ -86,25 +86,24 @@ class SimpleHTTPServerTest(unittest.TestCase): 'http://localhost:{0}/foo'.format(self.port), verify=False) self.assertEqual(response.status_code, http_client.NOT_FOUND) - def _test_simple_http(self, add): - chall = challenges.SimpleHTTP(token=(b'x' * 16)) - response = challenges.SimpleHTTPResponse(tls=False) + def _test_http01(self, add): + chall = challenges.HTTP01(token=(b'x' * 16)) + response, validation = chall.response_and_validation(self.account_key) - from acme.standalone import SimpleHTTPRequestHandler - resource = SimpleHTTPRequestHandler.SimpleHTTPResource( - chall=chall, response=response, validation=response.gen_validation( - chall, self.account_key)) + from acme.standalone import HTTP01RequestHandler + resource = HTTP01RequestHandler.HTTP01Resource( + chall=chall, response=response, validation=validation) if add: self.resources.add(resource) return resource.response.simple_verify( resource.chall, 'localhost', self.account_key.public_key(), port=self.port) - def test_simple_http_found(self): - self.assertTrue(self._test_simple_http(add=True)) + def test_http01_found(self): + self.assertTrue(self._test_http01(add=True)) - def test_simple_http_not_found(self): - self.assertFalse(self._test_simple_http(add=False)) + def test_http01_not_found(self): + self.assertFalse(self._test_http01(add=False)) class TestSimpleDVSNIServer(unittest.TestCase): diff --git a/docs/using.rst b/docs/using.rst index 73c3fe140..312e55510 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -127,7 +127,7 @@ Officially supported plugins: Plugin A I Notes and status ========== = = ================================================================ standalone Y N Very stable. Uses port 80 (force by - ``--standalone-supported-challenges simpleHttp``) or 443 + ``--standalone-supported-challenges http-01``) or 443 (force by ``--standalone-supported-challenges dvsni``). apache Y Y Alpha. Automates Apache installation, works fairly well but on Debian-based distributions only for now. diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py index ccff03319..c45337507 100644 --- a/letsencrypt/plugins/standalone.py +++ b/letsencrypt/plugins/standalone.py @@ -14,7 +14,6 @@ from acme import challenges from acme import crypto_util as acme_crypto_util from acme import standalone as acme_standalone -from letsencrypt import achallenges from letsencrypt import errors from letsencrypt import interfaces @@ -33,7 +32,7 @@ class ServerManager(object): `acme.crypto_util.SSLSocket.certs` and `acme.crypto_util.SSLSocket.simple_http_resources` respectively. All created servers share the same certificates and resources, so if - you're running both TLS and non-TLS instances, SimpleHTTP handlers + you're running both TLS and non-TLS instances, HTTP01 handlers will serve the same URLs! """ @@ -52,13 +51,13 @@ class ServerManager(object): :param int port: Port to run the server on. :param challenge_type: Subclass of `acme.challenges.Challenge`, - either `acme.challenge.SimpleHTTP` or `acme.challenges.DVSNI`. + either `acme.challenge.HTTP01` or `acme.challenges.DVSNI`. :returns: Server instance. :rtype: ACMEServerMixin """ - assert challenge_type in (challenges.DVSNI, challenges.SimpleHTTP) + assert challenge_type in (challenges.DVSNI, challenges.HTTP01) if port in self._instances: return self._instances[port].server @@ -66,8 +65,8 @@ class ServerManager(object): try: if challenge_type is challenges.DVSNI: server = acme_standalone.DVSNIServer(address, self.certs) - else: # challenges.SimpleHTTP - server = acme_standalone.SimpleHTTPServer( + else: # challenges.HTTP01 + server = acme_standalone.HTTP01Server( address, self.simple_http_resources) except socket.error as error: raise errors.StandaloneBindError(error, port) @@ -110,7 +109,7 @@ class ServerManager(object): in six.iteritems(self._instances)) -SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.SimpleHTTP]) +SUPPORTED_CHALLENGES = set([challenges.DVSNI, challenges.HTTP01]) def supported_challenges_validator(data): @@ -139,7 +138,7 @@ class Authenticator(common.Plugin): """Standalone Authenticator. This authenticator creates its own ephemeral TCP listener on the - necessary port in order to respond to incoming DVSNI and SimpleHTTP + necessary port in order to respond to incoming DVSNI and HTTP01 challenges from the certificate authority. Therefore, it does not rely on any existing server program. """ @@ -151,10 +150,10 @@ class Authenticator(common.Plugin): def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) - # one self-signed key for all DVSNI and SimpleHTTP certificates + # one self-signed key for all DVSNI and HTTP01 certificates self.key = OpenSSL.crypto.PKey() self.key.generate_key(OpenSSL.crypto.TYPE_RSA, bits=2048) - # TODO: generate only when the first SimpleHTTP challenge is solved + # TODO: generate only when the first HTTP01 challenge is solved self.simple_http_cert = acme_crypto_util.gen_ss_cert( self.key, domains=["temp server"]) @@ -185,7 +184,7 @@ class Authenticator(common.Plugin): @property def _necessary_ports(self): necessary_ports = set() - if challenges.SimpleHTTP in self.supported_challenges: + if challenges.HTTP01 in self.supported_challenges: necessary_ports.add(self.config.simple_http_port) if challenges.DVSNI in self.supported_challenges: necessary_ports.add(self.config.dvsni_port) @@ -193,9 +192,9 @@ class Authenticator(common.Plugin): def more_info(self): # pylint: disable=missing-docstring return("This authenticator creates its own ephemeral TCP listener " - "on the necessary port in order to respond to incoming DVSNI " - "and SimpleHTTP challenges from the certificate authority. " - "Therefore, it does not rely on any existing server program.") + "on the necessary port in order to respond to incoming DVSNI " + "and HTTP01 challenges from the certificate authority. " + "Therefore, it does not rely on any existing server program.") def prepare(self): # pylint: disable=missing-docstring pass @@ -237,13 +236,12 @@ class Authenticator(common.Plugin): responses = [] for achall in achalls: - if isinstance(achall, achallenges.SimpleHTTP): + if isinstance(achall.chall, challenges.HTTP01): server = self.servers.run( - self.config.simple_http_port, challenges.SimpleHTTP) - response, validation = achall.gen_response_and_validation( - tls=False) + self.config.simple_http_port, challenges.HTTP01) + response, validation = achall.response_and_validation() self.simple_http_resources.add( - acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource( + acme_standalone.HTTP01RequestHandler.HTTP01Resource( chall=achall.chall, response=response, validation=validation)) cert = self.simple_http_cert diff --git a/letsencrypt/plugins/standalone_test.py b/letsencrypt/plugins/standalone_test.py index 3a6941be8..6bf84cb73 100644 --- a/letsencrypt/plugins/standalone_test.py +++ b/letsencrypt/plugins/standalone_test.py @@ -43,12 +43,12 @@ class ServerManagerTest(unittest.TestCase): self._test_run_stop(challenges.DVSNI) def test_run_stop_simplehttp(self): - self._test_run_stop(challenges.SimpleHTTP) + self._test_run_stop(challenges.HTTP01) def test_run_idempotent(self): - server = self.mgr.run(port=0, challenge_type=challenges.SimpleHTTP) + server = self.mgr.run(port=0, challenge_type=challenges.HTTP01) port = server.socket.getsockname()[1] # pylint: disable=no-member - server2 = self.mgr.run(port=port, challenge_type=challenges.SimpleHTTP) + server2 = self.mgr.run(port=port, challenge_type=challenges.HTTP01) self.assertEqual(self.mgr.running(), {port: server}) self.assertTrue(server is server2) self.mgr.stop(port) @@ -60,7 +60,7 @@ class ServerManagerTest(unittest.TestCase): port = some_server.getsockname()[1] self.assertRaises( errors.StandaloneBindError, self.mgr.run, port, - challenge_type=challenges.SimpleHTTP) + challenge_type=challenges.HTTP01) self.assertEqual(self.mgr.running(), {}) @@ -74,9 +74,9 @@ class SupportedChallengesValidatorTest(unittest.TestCase): def test_correct(self): self.assertEqual("dvsni", self._call("dvsni")) - self.assertEqual("simpleHttp", self._call("simpleHttp")) - self.assertEqual("dvsni,simpleHttp", self._call("dvsni,simpleHttp")) - self.assertEqual("simpleHttp,dvsni", self._call("simpleHttp,dvsni")) + self.assertEqual("http-01", self._call("http-01")) + self.assertEqual("dvsni,http-01", self._call("dvsni,http-01")) + self.assertEqual("http-01,dvsni", self._call("http-01,dvsni")) def test_unrecognized(self): assert "foo" not in challenges.Challenge.TYPES @@ -91,25 +91,26 @@ class AuthenticatorTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone import Authenticator - self.config = mock.MagicMock(dvsni_port=1234, simple_http_port=4321, - standalone_supported_challenges="dvsni,simpleHttp") + self.config = mock.MagicMock( + dvsni_port=1234, simple_http_port=4321, + standalone_supported_challenges="dvsni,http-01") self.auth = Authenticator(self.config, name="standalone") def test_supported_challenges(self): self.assertEqual(self.auth.supported_challenges, - set([challenges.DVSNI, challenges.SimpleHTTP])) + set([challenges.DVSNI, challenges.HTTP01])) def test_more_info(self): self.assertTrue(isinstance(self.auth.more_info(), six.string_types)) def test_get_chall_pref(self): self.assertEqual(set(self.auth.get_chall_pref(domain=None)), - set([challenges.DVSNI, challenges.SimpleHTTP])) + set([challenges.DVSNI, challenges.HTTP01])) @mock.patch("letsencrypt.plugins.standalone.util") def test_perform_alredy_listening(self, mock_util): for chall, port in ((challenges.DVSNI.typ, 1234), - (challenges.SimpleHTTP.typ, 4321)): + (challenges.HTTP01.typ, 4321)): mock_util.already_listening.return_value = True self.config.standalone_supported_challenges = chall self.assertRaises( @@ -152,8 +153,8 @@ class AuthenticatorTest(unittest.TestCase): def test_perform2(self): domain = b'localhost' key = jose.JWK.load(test_util.load_vector('rsa512_key.pem')) - simple_http = achallenges.SimpleHTTP( - challb=acme_util.SIMPLE_HTTP_P, domain=domain, account_key=key) + simple_http = achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, domain=domain, account_key=key) dvsni = achallenges.DVSNI( challb=acme_util.DVSNI_P, domain=domain, account_key=key) @@ -167,11 +168,11 @@ class AuthenticatorTest(unittest.TestCase): self.assertTrue(isinstance(responses, list)) self.assertEqual(2, len(responses)) - self.assertTrue(isinstance(responses[0], challenges.SimpleHTTPResponse)) + self.assertTrue(isinstance(responses[0], challenges.HTTP01Response)) self.assertTrue(isinstance(responses[1], challenges.DVSNIResponse)) self.assertEqual(self.auth.servers.run.mock_calls, [ - mock.call(4321, challenges.SimpleHTTP), + mock.call(4321, challenges.HTTP01), mock.call(1234, challenges.DVSNI), ]) self.assertEqual(self.auth.served, { @@ -181,8 +182,8 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(1, len(self.auth.simple_http_resources)) self.assertEqual(2, len(self.auth.certs)) self.assertEqual(list(self.auth.simple_http_resources), [ - acme_standalone.SimpleHTTPRequestHandler.SimpleHTTPResource( - acme_util.SIMPLE_HTTP, responses[0], mock.ANY)]) + acme_standalone.HTTP01RequestHandler.HTTP01Resource( + acme_util.HTTP01, responses[0], mock.ANY)]) def test_cleanup(self): self.auth.servers = mock.Mock() diff --git a/tests/boulder-integration.sh b/tests/boulder-integration.sh index 7c0ee8fea..18a996926 100755 --- a/tests/boulder-integration.sh +++ b/tests/boulder-integration.sh @@ -28,7 +28,7 @@ common() { } common --domains le1.wtf --standalone-supported-challenges dvsni auth -common --domains le2.wtf --standalone-supported-challenges simpleHttp run +common --domains le2.wtf --standalone-supported-challenges http-01 run common -a manual -d le.wtf auth export CSR_PATH="${root}/csr.der" KEY_PATH="${root}/key.pem" \ From e62490c0514a6883d9b91aa5d4d6c6747f0364e1 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 15:02:47 +0000 Subject: [PATCH 204/216] http-01 for webroot --- letsencrypt/plugins/webroot.py | 13 ++++++------- letsencrypt/plugins/webroot_test.py | 13 ++++++++----- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index f11325f57..879da2527 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -23,7 +23,7 @@ class Authenticator(common.Plugin): description = "Webroot Authenticator" MORE_INFO = """\ -Authenticator plugin that performs SimpleHTTP challenge by saving +Authenticator plugin that performs http-01 challenge by saving necessary validation resources to appropriate paths on the file system. It expects that there is some other HTTP server configured to serve all files under specified web root ({0}).""" @@ -37,7 +37,7 @@ to serve all files under specified web root ({0}).""" def get_chall_pref(self, domain): # pragma: no cover # pylint: disable=missing-docstring,no-self-use,unused-argument - return [challenges.SimpleHTTP] + return [challenges.HTTP01] def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) @@ -51,8 +51,7 @@ to serve all files under specified web root ({0}).""" if not os.path.isdir(path): raise errors.PluginError( path + " does not exist or is not a directory") - self.full_root = os.path.join( - path, challenges.SimpleHTTPResponse.URI_ROOT_PATH) + self.full_root = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", self.full_root) @@ -61,7 +60,7 @@ to serve all files under specified web root ({0}).""" except OSError as exception: if exception.errno != errno.EEXIST: raise errors.PluginError( - "Couldn't create root for SimpleHTTP " + "Couldn't create root for http-01 " "challenge responses: {0}", exception) def perform(self, achalls): # pylint: disable=missing-docstring @@ -72,11 +71,11 @@ to serve all files under specified web root ({0}).""" return os.path.join(self.full_root, achall.chall.encode("token")) def _perform_single(self, achall): - response, validation = achall.gen_response_and_validation(tls=False) + response, validation = achall.response_and_validation() path = self._path_for_achall(achall) logger.debug("Attempting to save validation to %s", path) with open(path, "w") as validation_file: - validation_file.write(validation.json_dumps()) + validation_file.write(validation.encode()) return response def cleanup(self, achalls): # pylint: disable=missing-docstring diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index d8c0e2aa2..aa8f16e38 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -6,6 +6,7 @@ import unittest import mock +from acme import challenges from acme import jose from letsencrypt import achallenges @@ -21,8 +22,8 @@ KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class AuthenticatorTest(unittest.TestCase): """Tests for letsencrypt.plugins.webroot.Authenticator.""" - achall = achallenges.SimpleHTTP( - challb=acme_util.SIMPLE_HTTP_P, domain=None, account_key=KEY) + achall = achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, domain=None, account_key=KEY) def setUp(self): from letsencrypt.plugins.webroot import Authenticator @@ -70,9 +71,11 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(1, len(responses)) self.assertTrue(os.path.exists(self.validation_path)) with open(self.validation_path) as validation_f: - validation = jose.JWS.json_loads(validation_f.read()) - self.assertTrue(responses[0].check_validation( - validation, self.achall.chall, KEY.public_key())) + validation = validation_f.read() + self.assertTrue( + challenges.KeyAuthorizationChallengeResponse( + key_authorization=validation).verify( + self.achall.chall, KEY.public_key())) self.auth.cleanup([self.achall]) self.assertFalse(os.path.exists(self.validation_path)) From 8d913b42c56f2d5998a7c69a5926687768a74277 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 22:12:17 +0000 Subject: [PATCH 205/216] Remove auth_handler_test.TRANSLATE --- letsencrypt/tests/auth_handler_test.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 247fca4e1..8a4c371f5 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -16,15 +16,6 @@ from letsencrypt import le_util from letsencrypt.tests import acme_util -TRANSLATE = { - "dvsni": "DVSNI", - "simpleHttp": "SimpleHTTP", - "dns": "DNS", - "recoveryContact": "RecoveryContact", - "proofOfPossession": "ProofOfPossession", -} - - class ChallengeFactoryTest(unittest.TestCase): # pylint: disable=protected-access From 581a701cd19c7b7236d018109ace58e5a9ba75c8 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 22:23:57 +0000 Subject: [PATCH 206/216] Kill simpleHttp in core --- letsencrypt/achallenges.py | 25 ------------------------- letsencrypt/auth_handler.py | 3 --- letsencrypt/configuration.py | 4 ++-- letsencrypt/constants.py | 2 +- letsencrypt/tests/acme_util.py | 7 ++----- letsencrypt/tests/auth_handler_test.py | 24 ++++++++++++------------ 6 files changed, 17 insertions(+), 48 deletions(-) diff --git a/letsencrypt/achallenges.py b/letsencrypt/achallenges.py index 7aa2bd35a..f08c6a396 100644 --- a/letsencrypt/achallenges.py +++ b/letsencrypt/achallenges.py @@ -85,31 +85,6 @@ class DVSNI(AnnotatedChallenge): return response, cert, key -class SimpleHTTP(AnnotatedChallenge): - """Client annotated "simpleHttp" ACME challenge.""" - __slots__ = ('challb', 'domain', 'account_key') - acme_type = challenges.SimpleHTTP - - def gen_response_and_validation(self, tls): - """Generates a SimpleHTTP response and validation. - - :param bool tls: True if TLS should be used - - :returns: ``(response, validation)`` tuple, where ``response`` is - an instance of `acme.challenges.SimpleHTTPResponse` and - ``validation`` is an instance of - `acme.challenges.SimpleHTTPProvisionedResource`. - :rtype: tuple - - """ - response = challenges.SimpleHTTPResponse(tls=tls) - - validation = response.gen_validation( - self.challb.chall, self.account_key) - logger.debug("Simple HTTP validation payload: %s", validation.payload) - return response, validation - - class DNS(AnnotatedChallenge): """Client annotated "dns" ACME challenge.""" __slots__ = ('challb', 'domain') diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index f7b6bd507..11019daac 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -347,9 +347,6 @@ def challb_to_achall(challb, account_key, domain): if isinstance(chall, challenges.DVSNI): return achallenges.DVSNI( challb=challb, domain=domain, account_key=account_key) - elif isinstance(chall, challenges.SimpleHTTP): - return achallenges.SimpleHTTP( - challb=challb, domain=domain, account_key=account_key) elif isinstance(chall, challenges.DNS): return achallenges.DNS(challb=challb, domain=domain) elif isinstance(chall, challenges.RecoveryContact): diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index f72005233..4fb417127 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -39,7 +39,7 @@ class NamespaceConfig(object): if self.simple_http_port == self.dvsni_port: raise errors.Error( - "Trying to run SimpleHTTP and DVSNI " + "Trying to run http-01 and DVSNI " "on the same port ({0})".format(self.dvsni_port)) def __getattr__(self, name): @@ -82,7 +82,7 @@ class NamespaceConfig(object): if self.namespace.simple_http_port is not None: return self.namespace.simple_http_port else: - return challenges.SimpleHTTPResponse.PORT + return challenges.HTTP01Response.PORT class RenewerConfiguration(object): diff --git a/letsencrypt/constants.py b/letsencrypt/constants.py index 362009ec6..15f8c53f0 100644 --- a/letsencrypt/constants.py +++ b/letsencrypt/constants.py @@ -41,7 +41,7 @@ RENEWER_DEFAULTS = dict( EXCLUSIVE_CHALLENGES = frozenset([frozenset([ - challenges.DVSNI, challenges.SimpleHTTP])]) + challenges.DVSNI, challenges.HTTP01])]) """Mutually exclusive challenges.""" diff --git a/letsencrypt/tests/acme_util.py b/letsencrypt/tests/acme_util.py index 1e3086b18..300eb453b 100644 --- a/letsencrypt/tests/acme_util.py +++ b/letsencrypt/tests/acme_util.py @@ -12,8 +12,6 @@ from letsencrypt.tests import test_util KEY = test_util.load_rsa_private_key('rsa512_key.pem') # Challenges -SIMPLE_HTTP = challenges.SimpleHTTP( - token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA") HTTP01 = challenges.HTTP01( token="evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA") DVSNI = challenges.DVSNI( @@ -43,7 +41,7 @@ POP = challenges.ProofOfPossession( ) ) -CHALLENGES = [SIMPLE_HTTP, DVSNI, DNS, RECOVERY_CONTACT, POP] +CHALLENGES = [HTTP01, DVSNI, DNS, RECOVERY_CONTACT, POP] DV_CHALLENGES = [chall for chall in CHALLENGES if isinstance(chall, challenges.DVChallenge)] CONT_CHALLENGES = [chall for chall in CHALLENGES @@ -82,13 +80,12 @@ def chall_to_challb(chall, status): # pylint: disable=redefined-outer-name # Pending ChallengeBody objects DVSNI_P = chall_to_challb(DVSNI, messages.STATUS_PENDING) -SIMPLE_HTTP_P = chall_to_challb(SIMPLE_HTTP, messages.STATUS_PENDING) HTTP01_P = chall_to_challb(HTTP01, messages.STATUS_PENDING) DNS_P = chall_to_challb(DNS, messages.STATUS_PENDING) RECOVERY_CONTACT_P = chall_to_challb(RECOVERY_CONTACT, messages.STATUS_PENDING) POP_P = chall_to_challb(POP, messages.STATUS_PENDING) -CHALLENGES_P = [SIMPLE_HTTP_P, DVSNI_P, DNS_P, RECOVERY_CONTACT_P, POP_P] +CHALLENGES_P = [HTTP01_P, DVSNI_P, DNS_P, RECOVERY_CONTACT_P, POP_P] DV_CHALLENGES_P = [challb for challb in CHALLENGES_P if isinstance(challb.chall, challenges.DVChallenge)] CONT_CHALLENGES_P = [ diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 8a4c371f5..7be37c91e 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -309,8 +309,8 @@ class GenChallengePathTest(unittest.TestCase): return gen_challenge_path(challbs, preferences, combinations) def test_common_case(self): - """Given DVSNI and SimpleHTTP with appropriate combos.""" - challbs = (acme_util.DVSNI_P, acme_util.SIMPLE_HTTP_P) + """Given DVSNI and HTTP01 with appropriate combos.""" + challbs = (acme_util.DVSNI_P, acme_util.HTTP01_P) prefs = [challenges.DVSNI] combos = ((0,), (1,)) @@ -325,7 +325,7 @@ class GenChallengePathTest(unittest.TestCase): challbs = (acme_util.POP_P, acme_util.RECOVERY_CONTACT_P, acme_util.DVSNI_P, - acme_util.SIMPLE_HTTP_P) + acme_util.HTTP01_P) prefs = [challenges.ProofOfPossession, challenges.DVSNI] combos = acme_util.gen_combos(challbs) self.assertEqual(self._call(challbs, prefs, combos), (0, 2)) @@ -337,12 +337,12 @@ class GenChallengePathTest(unittest.TestCase): challbs = (acme_util.RECOVERY_CONTACT_P, acme_util.POP_P, acme_util.DVSNI_P, - acme_util.SIMPLE_HTTP_P, + acme_util.HTTP01_P, acme_util.DNS_P) # Typical webserver client that can do everything except DNS # Attempted to make the order realistic prefs = [challenges.ProofOfPossession, - challenges.SimpleHTTP, + challenges.HTTP01, challenges.DVSNI, challenges.RecoveryContact] combos = acme_util.gen_combos(challbs) @@ -411,8 +411,8 @@ class IsPreferredTest(unittest.TestCase): def _call(cls, chall, satisfied): from letsencrypt.auth_handler import is_preferred return is_preferred(chall, satisfied, exclusive_groups=frozenset([ - frozenset([challenges.DVSNI, challenges.SimpleHTTP]), - frozenset([challenges.DNS, challenges.SimpleHTTP]), + frozenset([challenges.DVSNI, challenges.HTTP01]), + frozenset([challenges.DNS, challenges.HTTP01]), ])) def test_empty_satisfied(self): @@ -421,7 +421,7 @@ class IsPreferredTest(unittest.TestCase): def test_mutually_exclusvie(self): self.assertFalse( self._call( - acme_util.DVSNI_P, frozenset([acme_util.SIMPLE_HTTP_P]))) + acme_util.DVSNI_P, frozenset([acme_util.HTTP01_P]))) def test_mutually_exclusive_same_type(self): self.assertTrue( @@ -434,13 +434,13 @@ class ReportFailedChallsTest(unittest.TestCase): def setUp(self): kwargs = { - "chall": acme_util.SIMPLE_HTTP, + "chall": acme_util.HTTP01, "uri": "uri", "status": messages.STATUS_INVALID, "error": messages.Error(typ="tls", detail="detail"), } - self.simple_http = achallenges.SimpleHTTP( + self.http01 = achallenges.KeyAuthorizationAnnotatedChallenge( # pylint: disable=star-args challb=messages.ChallengeBody(**kwargs), domain="example.com", @@ -464,7 +464,7 @@ class ReportFailedChallsTest(unittest.TestCase): def test_same_error_and_domain(self, mock_zope): from letsencrypt import auth_handler - auth_handler._report_failed_challs([self.simple_http, self.dvsni_same]) + auth_handler._report_failed_challs([self.http01, self.dvsni_same]) call_list = mock_zope().add_message.call_args_list self.assertTrue(len(call_list) == 1) self.assertTrue("Domains: example.com\n" in call_list[0][0][0]) @@ -473,7 +473,7 @@ class ReportFailedChallsTest(unittest.TestCase): def test_different_errors_and_domains(self, mock_zope): from letsencrypt import auth_handler - auth_handler._report_failed_challs([self.simple_http, self.dvsni_diff]) + auth_handler._report_failed_challs([self.http01, self.dvsni_diff]) self.assertTrue(mock_zope().add_message.call_count == 2) From 063544817b9138d7cf7fcb7806977e532b67c580 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 1 Nov 2015 08:46:00 +0000 Subject: [PATCH 207/216] Kill simplehttp apache/nginx --- letsencrypt-apache/letsencrypt_apache/dvsni.py | 2 +- letsencrypt-nginx/letsencrypt_nginx/dvsni.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/dvsni.py b/letsencrypt-apache/letsencrypt_apache/dvsni.py index c6c41dc51..ed88bf8a7 100644 --- a/letsencrypt-apache/letsencrypt_apache/dvsni.py +++ b/letsencrypt-apache/letsencrypt_apache/dvsni.py @@ -20,7 +20,7 @@ class ApacheDvsni(common.Dvsni): larger array. ApacheDvsni is capable of solving many challenges at once which causes an indexing issue within ApacheConfigurator who must return all responses in order. Imagine ApacheConfigurator - maintaining state about where all of the SimpleHTTP Challenges, + maintaining state about where all of the http-01 Challenges, Dvsni Challenges belong in the response array. This is an optional utility. diff --git a/letsencrypt-nginx/letsencrypt_nginx/dvsni.py b/letsencrypt-nginx/letsencrypt_nginx/dvsni.py index 9ac2fcd7c..662f10889 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/dvsni.py +++ b/letsencrypt-nginx/letsencrypt_nginx/dvsni.py @@ -26,7 +26,7 @@ class NginxDvsni(common.Dvsni): larger array. NginxDvsni is capable of solving many challenges at once which causes an indexing issue within NginxConfigurator who must return all responses in order. Imagine NginxConfigurator - maintaining state about where all of the SimpleHTTP Challenges, + maintaining state about where all of the http-01 Challenges, Dvsni Challenges belong in the response array. This is an optional utility. From 1d36a15ab702594072a1b46a379de1e1594b2475 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 1 Nov 2015 10:41:57 +0000 Subject: [PATCH 208/216] Kill simpleHttp in acme --- acme/acme/challenges.py | 160 --------------------------------- acme/acme/challenges_test.py | 169 ----------------------------------- 2 files changed, 329 deletions(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index 86ff0a902..b424071a1 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -318,166 +318,6 @@ class HTTP01(KeyAuthorizationChallenge): return self.key_authorization(account_key) -@Challenge.register # pylint: disable=too-many-ancestors -class SimpleHTTP(_TokenDVChallenge): - """ACME "simpleHttp" challenge.""" - typ = "simpleHttp" - - URI_ROOT_PATH = ".well-known/acme-challenge" - """URI root path for the server provisioned resource.""" - - @property - def path(self): - """Path (starting with '/') for provisioned resource.""" - return '/' + self.URI_ROOT_PATH + '/' + self.encode('token') - - -@ChallengeResponse.register -class SimpleHTTPResponse(ChallengeResponse): - """ACME "simpleHttp" challenge response. - - :ivar bool tls: - - """ - typ = "simpleHttp" - tls = jose.Field("tls", default=True, omitempty=True) - - URI_ROOT_PATH = SimpleHTTP.URI_ROOT_PATH - _URI_TEMPLATE = "{scheme}://{domain}/" + URI_ROOT_PATH + "/{token}" - - CONTENT_TYPE = "application/jose+json" - PORT = 80 - TLS_PORT = 443 - - @property - def scheme(self): - """URL scheme for the provisioned resource.""" - return "https" if self.tls else "http" - - @property - def port(self): - """Port that the ACME client should be listening for validation.""" - return self.TLS_PORT if self.tls else self.PORT - - def uri(self, domain, chall): - """Create an URI to the provisioned resource. - - Forms an URI to the HTTPS server provisioned resource - (containing :attr:`~SimpleHTTP.token`). - - :param unicode domain: Domain name being verified. - :param challenges.SimpleHTTP chall: - - """ - return self._URI_TEMPLATE.format( - scheme=self.scheme, domain=domain, token=chall.encode("token")) - - def gen_resource(self, chall): - """Generate provisioned resource. - - :param challenges.SimpleHTTP chall: - :rtype: SimpleHTTPProvisionedResource - - """ - return SimpleHTTPProvisionedResource(token=chall.token, tls=self.tls) - - def gen_validation(self, chall, account_key, alg=jose.RS256, **kwargs): - """Generate validation. - - :param challenges.SimpleHTTP chall: - :param .JWK account_key: Private account key. - :param .JWA alg: - - :returns: `.SimpleHTTPProvisionedResource` signed in `.JWS` - :rtype: .JWS - - """ - return jose.JWS.sign( - payload=self.gen_resource(chall).json_dumps( - sort_keys=True).encode('utf-8'), - key=account_key, alg=alg, **kwargs) - - def check_validation(self, validation, chall, account_public_key): - """Check validation. - - :param .JWS validation: - :param challenges.SimpleHTTP chall: - :param .JWK account_public_key: - - :rtype: bool - - """ - if not validation.verify(key=account_public_key): - return False - - try: - resource = SimpleHTTPProvisionedResource.json_loads( - validation.payload.decode('utf-8')) - except jose.DeserializationError as error: - logger.debug(error) - return False - - return resource.token == chall.token and resource.tls == self.tls - - def simple_verify(self, chall, domain, account_public_key, port=None): - """Simple verify. - - According to the ACME specification, "the ACME server MUST - ignore the certificate provided by the HTTPS server", so - ``requests.get`` is called with ``verify=False``. - - :param challenges.SimpleHTTP chall: Corresponding challenge. - :param unicode domain: Domain name being verified. - :param JWK account_public_key: Public key for the key pair - being authorized. If ``None`` key verification is not - performed! - :param int port: Port used in the validation. - - :returns: ``True`` iff validation is successful, ``False`` - otherwise. - :rtype: bool - - """ - # TODO: ACME specification defines URI template that doesn't - # allow to use a custom port... Make sure port is not in the - # request URI, if it's standard. - if port is not None and port != self.port: - logger.warning( - "Using non-standard port for SimpleHTTP verification: %s", port) - domain += ":{0}".format(port) - - uri = self.uri(domain, chall) - logger.debug("Verifying %s at %s...", chall.typ, uri) - try: - http_response = requests.get(uri, verify=False) - except requests.exceptions.RequestException as error: - logger.error("Unable to reach %s: %s", uri, error) - return False - logger.debug("Received %s: %s. Headers: %s", http_response, - http_response.text, http_response.headers) - - if self.CONTENT_TYPE != http_response.headers.get( - "Content-Type", self.CONTENT_TYPE): - return False - - try: - validation = jose.JWS.json_loads(http_response.text) - except jose.DeserializationError as error: - logger.debug(error) - return False - - return self.check_validation(validation, chall, account_public_key) - - -class SimpleHTTPProvisionedResource(jose.JSONObjectWithFields): - """SimpleHTTP provisioned resource.""" - typ = fields.Fixed("type", SimpleHTTP.typ) - token = SimpleHTTP._fields["token"] - # If the "tls" field is not included in the response, then - # validation object MUST have its "tls" field set to "true". - tls = jose.Field("tls", omitempty=False) - - @Challenge.register # pylint: disable=too-many-ancestors class DVSNI(_TokenDVChallenge): """ACME "dvsni" challenge. diff --git a/acme/acme/challenges_test.py b/acme/acme/challenges_test.py index 53e6b528a..0708f3782 100644 --- a/acme/acme/challenges_test.py +++ b/acme/acme/challenges_test.py @@ -186,175 +186,6 @@ class HTTP01Test(unittest.TestCase): self.msg.update(token=b'..').good_token) -class SimpleHTTPTest(unittest.TestCase): - - def setUp(self): - from acme.challenges import SimpleHTTP - self.msg = SimpleHTTP( - token=jose.decode_b64jose( - 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+oA')) - self.jmsg = { - 'type': 'simpleHttp', - 'token': 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA', - } - - def test_path(self): - self.assertEqual(self.msg.path, '/.well-known/acme-challenge/' - 'evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA') - - def test_to_partial_json(self): - self.assertEqual(self.jmsg, self.msg.to_partial_json()) - - def test_from_json(self): - from acme.challenges import SimpleHTTP - self.assertEqual(self.msg, SimpleHTTP.from_json(self.jmsg)) - - def test_from_json_hashable(self): - from acme.challenges import SimpleHTTP - hash(SimpleHTTP.from_json(self.jmsg)) - - def test_good_token(self): - self.assertTrue(self.msg.good_token) - self.assertFalse( - self.msg.update(token=b'..').good_token) - - -class SimpleHTTPResponseTest(unittest.TestCase): - # pylint: disable=too-many-instance-attributes - - def setUp(self): - from acme.challenges import SimpleHTTPResponse - self.msg_http = SimpleHTTPResponse(tls=False) - self.msg_https = SimpleHTTPResponse(tls=True) - self.jmsg_http = { - 'resource': 'challenge', - 'type': 'simpleHttp', - 'tls': False, - } - self.jmsg_https = { - 'resource': 'challenge', - 'type': 'simpleHttp', - 'tls': True, - } - - from acme.challenges import SimpleHTTP - self.chall = SimpleHTTP(token=(b"x" * 16)) - self.resp_http = SimpleHTTPResponse(tls=False) - self.resp_https = SimpleHTTPResponse(tls=True) - self.good_headers = {'Content-Type': SimpleHTTPResponse.CONTENT_TYPE} - - def test_to_partial_json(self): - self.assertEqual(self.jmsg_http, self.msg_http.to_partial_json()) - self.assertEqual(self.jmsg_https, self.msg_https.to_partial_json()) - - def test_from_json(self): - from acme.challenges import SimpleHTTPResponse - self.assertEqual( - self.msg_http, SimpleHTTPResponse.from_json(self.jmsg_http)) - self.assertEqual( - self.msg_https, SimpleHTTPResponse.from_json(self.jmsg_https)) - - def test_from_json_hashable(self): - from acme.challenges import SimpleHTTPResponse - hash(SimpleHTTPResponse.from_json(self.jmsg_http)) - hash(SimpleHTTPResponse.from_json(self.jmsg_https)) - - def test_scheme(self): - self.assertEqual('http', self.msg_http.scheme) - self.assertEqual('https', self.msg_https.scheme) - - def test_port(self): - self.assertEqual(80, self.msg_http.port) - self.assertEqual(443, self.msg_https.port) - - def test_uri(self): - self.assertEqual( - 'http://example.com/.well-known/acme-challenge/' - 'eHh4eHh4eHh4eHh4eHh4eA', self.msg_http.uri( - 'example.com', self.chall)) - self.assertEqual( - 'https://example.com/.well-known/acme-challenge/' - 'eHh4eHh4eHh4eHh4eHh4eA', self.msg_https.uri( - 'example.com', self.chall)) - - def test_gen_check_validation(self): - account_key = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem')) - self.assertTrue(self.resp_http.check_validation( - validation=self.resp_http.gen_validation(self.chall, account_key), - chall=self.chall, account_public_key=account_key.public_key())) - - def test_gen_check_validation_wrong_key(self): - key1 = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem')) - key2 = jose.JWKRSA.load(test_util.load_vector('rsa1024_key.pem')) - self.assertFalse(self.resp_http.check_validation( - validation=self.resp_http.gen_validation(self.chall, key1), - chall=self.chall, account_public_key=key2.public_key())) - - def test_check_validation_wrong_payload(self): - account_key = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem')) - validations = tuple( - jose.JWS.sign(payload=payload, alg=jose.RS256, key=account_key) - for payload in (b'', b'{}', self.chall.json_dumps().encode('utf-8'), - self.resp_http.json_dumps().encode('utf-8')) - ) - for validation in validations: - self.assertFalse(self.resp_http.check_validation( - validation=validation, chall=self.chall, - account_public_key=account_key.public_key())) - - def test_check_validation_wrong_fields(self): - resource = self.resp_http.gen_resource(self.chall) - account_key = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem')) - validations = tuple( - jose.JWS.sign(payload=bad_resource.json_dumps().encode('utf-8'), - alg=jose.RS256, key=account_key) - for bad_resource in (resource.update(tls=True), - resource.update(token=(b'x' * 20))) - ) - for validation in validations: - self.assertFalse(self.resp_http.check_validation( - validation=validation, chall=self.chall, - account_public_key=account_key.public_key())) - - @mock.patch("acme.challenges.requests.get") - def test_simple_verify_good_validation(self, mock_get): - account_key = jose.JWKRSA.load(test_util.load_vector('rsa512_key.pem')) - for resp in self.resp_http, self.resp_https: - mock_get.reset_mock() - validation = resp.gen_validation(self.chall, account_key) - mock_get.return_value = mock.MagicMock( - text=validation.json_dumps(), headers=self.good_headers) - self.assertTrue(resp.simple_verify(self.chall, "local", None)) - mock_get.assert_called_once_with(resp.uri( - "local", self.chall), verify=False) - - @mock.patch("acme.challenges.requests.get") - def test_simple_verify_bad_validation(self, mock_get): - mock_get.return_value = mock.MagicMock( - text="!", headers=self.good_headers) - self.assertFalse(self.resp_http.simple_verify( - self.chall, "local", None)) - - @mock.patch("acme.challenges.requests.get") - def test_simple_verify_bad_content_type(self, mock_get): - mock_get().text = self.chall.token - self.assertFalse(self.resp_http.simple_verify( - self.chall, "local", None)) - - @mock.patch("acme.challenges.requests.get") - def test_simple_verify_connection_error(self, mock_get): - mock_get.side_effect = requests.exceptions.RequestException - self.assertFalse(self.resp_http.simple_verify( - self.chall, "local", None)) - - @mock.patch("acme.challenges.requests.get") - def test_simple_verify_port(self, mock_get): - self.resp_http.simple_verify( - self.chall, domain="local", account_public_key=None, port=4430) - self.assertEqual("local:4430", urllib_parse.urlparse( - mock_get.mock_calls[0][1][0]).netloc) - - class DVSNITest(unittest.TestCase): def setUp(self): From 23d3c3b1e2bc5fbed5fb61c855e1a207ab1a9dda Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 31 Oct 2015 22:04:52 +0000 Subject: [PATCH 209/216] Rename --simple-http-port to --http-01-port --- letsencrypt/cli.py | 4 ++-- letsencrypt/configuration.py | 8 ++++---- letsencrypt/interfaces.py | 2 +- letsencrypt/plugins/manual.py | 6 +++--- letsencrypt/plugins/manual_test.py | 4 ++-- letsencrypt/plugins/standalone.py | 4 ++-- letsencrypt/plugins/standalone_test.py | 2 +- letsencrypt/renewer.py | 2 +- letsencrypt/tests/configuration_test.py | 10 +++++----- letsencrypt/tests/renewer_test.py | 2 +- tests/integration/_common.sh | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index fdfa5bbfb..f8c0da85a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -854,8 +854,8 @@ def prepare_and_parse_args(plugins, args): helpful.add( "testing", "--dvsni-port", type=int, default=flag_default("dvsni_port"), help=config_help("dvsni_port")) - helpful.add("testing", "--simple-http-port", type=int, - help=config_help("simple_http_port")) + helpful.add("testing", "--http-01-port", dest="http01_port", type=int, + help=config_help("http01_port")) helpful.add_group( "security", description="Security parameters & server settings") diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index 4fb417127..b604651e9 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -37,7 +37,7 @@ class NamespaceConfig(object): def __init__(self, namespace): self.namespace = namespace - if self.simple_http_port == self.dvsni_port: + if self.http01_port == self.dvsni_port: raise errors.Error( "Trying to run http-01 and DVSNI " "on the same port ({0})".format(self.dvsni_port)) @@ -78,9 +78,9 @@ class NamespaceConfig(object): self.namespace.work_dir, constants.TEMP_CHECKPOINT_DIR) @property - def simple_http_port(self): # pylint: disable=missing-docstring - if self.namespace.simple_http_port is not None: - return self.namespace.simple_http_port + def http01_port(self): # pylint: disable=missing-docstring + if self.namespace.http01_port is not None: + return self.namespace.http01_port else: return challenges.HTTP01Response.PORT diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 8bf714c88..498b01683 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -223,7 +223,7 @@ class IConfig(zope.interface.Interface): "Port number to perform DVSNI challenge. " "Boulder in testing mode defaults to 5001.") - simple_http_port = zope.interface.Attribute( + http01_port = zope.interface.Attribute( "Port used in the SimpleHttp challenge.") diff --git a/letsencrypt/plugins/manual.py b/letsencrypt/plugins/manual.py index ffaad3d99..823de89ec 100644 --- a/letsencrypt/plugins/manual.py +++ b/letsencrypt/plugins/manual.py @@ -132,8 +132,8 @@ s.serve_forever()" """ # same server: default command doesn't support virtual hosts response, validation = achall.response_and_validation() - port = (response.port if self.config.simple_http_port is None - else int(self.config.simple_http_port)) + port = (response.port if self.config.http01_port is None + else int(self.config.http01_port)) command = self.CMD_TEMPLATE.format( root=self._root, achall=achall, response=response, # TODO(kuba): pipes still necessary? @@ -174,7 +174,7 @@ s.serve_forever()" """ if response.simple_verify( achall.chall, achall.domain, - achall.account_key.public_key(), self.config.simple_http_port): + achall.account_key.public_key(), self.config.http01_port): return response else: logger.error( diff --git a/letsencrypt/plugins/manual_test.py b/letsencrypt/plugins/manual_test.py index 431cd07a7..a9281902f 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/letsencrypt/plugins/manual_test.py @@ -23,13 +23,13 @@ class AuthenticatorTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.manual import Authenticator self.config = mock.MagicMock( - simple_http_port=8080, manual_test_mode=False) + http01_port=8080, manual_test_mode=False) self.auth = Authenticator(config=self.config, name="manual") self.achalls = [achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY)] config_test_mode = mock.MagicMock( - simple_http_port=8080, manual_test_mode=True) + http01_port=8080, manual_test_mode=True) self.auth_test_mode = Authenticator( config=config_test_mode, name="manual") diff --git a/letsencrypt/plugins/standalone.py b/letsencrypt/plugins/standalone.py index c45337507..5041091e4 100644 --- a/letsencrypt/plugins/standalone.py +++ b/letsencrypt/plugins/standalone.py @@ -185,7 +185,7 @@ class Authenticator(common.Plugin): def _necessary_ports(self): necessary_ports = set() if challenges.HTTP01 in self.supported_challenges: - necessary_ports.add(self.config.simple_http_port) + necessary_ports.add(self.config.http01_port) if challenges.DVSNI in self.supported_challenges: necessary_ports.add(self.config.dvsni_port) return necessary_ports @@ -238,7 +238,7 @@ class Authenticator(common.Plugin): for achall in achalls: if isinstance(achall.chall, challenges.HTTP01): server = self.servers.run( - self.config.simple_http_port, challenges.HTTP01) + self.config.http01_port, challenges.HTTP01) response, validation = achall.response_and_validation() self.simple_http_resources.add( acme_standalone.HTTP01RequestHandler.HTTP01Resource( diff --git a/letsencrypt/plugins/standalone_test.py b/letsencrypt/plugins/standalone_test.py index 6bf84cb73..15da04417 100644 --- a/letsencrypt/plugins/standalone_test.py +++ b/letsencrypt/plugins/standalone_test.py @@ -92,7 +92,7 @@ class AuthenticatorTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone import Authenticator self.config = mock.MagicMock( - dvsni_port=1234, simple_http_port=4321, + dvsni_port=1234, http01_port=4321, standalone_supported_challenges="dvsni,http-01") self.auth = Authenticator(self.config, name="standalone") diff --git a/letsencrypt/renewer.py b/letsencrypt/renewer.py index c0014f4b2..40e49702a 100644 --- a/letsencrypt/renewer.py +++ b/letsencrypt/renewer.py @@ -76,7 +76,7 @@ def renew(cert, old_version): # was an int, not a str) config.rsa_key_size = int(config.rsa_key_size) config.dvsni_port = int(config.dvsni_port) - config.namespace.simple_http_port = int(config.namespace.simple_http_port) + config.namespace.http01_port = int(config.namespace.http01_port) zope.component.provideUtility(config) try: authenticator = plugins[renewalparams["authenticator"]] diff --git a/letsencrypt/tests/configuration_test.py b/letsencrypt/tests/configuration_test.py index 44bccb577..c7e227ee5 100644 --- a/letsencrypt/tests/configuration_test.py +++ b/letsencrypt/tests/configuration_test.py @@ -14,7 +14,7 @@ class NamespaceConfigTest(unittest.TestCase): self.namespace = mock.MagicMock( config_dir='/tmp/config', work_dir='/tmp/foo', foo='bar', server='https://acme-server.org:443/new', - dvsni_port=1234, simple_http_port=4321) + dvsni_port=1234, http01_port=4321) from letsencrypt.configuration import NamespaceConfig self.config = NamespaceConfig(self.namespace) @@ -54,10 +54,10 @@ class NamespaceConfigTest(unittest.TestCase): self.assertEqual(self.config.key_dir, '/tmp/config/keys') self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t') - def test_simple_http_port(self): - self.assertEqual(4321, self.config.simple_http_port) - self.namespace.simple_http_port = None - self.assertEqual(80, self.config.simple_http_port) + def test_http01_port(self): + self.assertEqual(4321, self.config.http01_port) + self.namespace.http01_port = None + self.assertEqual(80, self.config.http01_port) class RenewerConfigurationTest(unittest.TestCase): diff --git a/letsencrypt/tests/renewer_test.py b/letsencrypt/tests/renewer_test.py index 2123db367..05d7e123d 100644 --- a/letsencrypt/tests/renewer_test.py +++ b/letsencrypt/tests/renewer_test.py @@ -689,7 +689,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.test_rc.configfile["renewalparams"]["server"] = "acme.example.com" self.test_rc.configfile["renewalparams"]["authenticator"] = "fake" self.test_rc.configfile["renewalparams"]["dvsni_port"] = "4430" - self.test_rc.configfile["renewalparams"]["simple_http_port"] = "1234" + self.test_rc.configfile["renewalparams"]["http01_port"] = "1234" self.test_rc.configfile["renewalparams"]["account"] = "abcde" mock_auth = mock.MagicMock() mock_pd.PluginsRegistry.find_all.return_value = {"apache": mock_auth} diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index 07eca44b2..cd894fd10 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -16,7 +16,7 @@ letsencrypt_test () { --server "${SERVER:-http://localhost:4000/directory}" \ --no-verify-ssl \ --dvsni-port 5001 \ - --simple-http-port 5002 \ + --http-01-port 5002 \ --manual-test-mode \ $store_flags \ --text \ From 99c5c2034fd54aacef1b29794e0551bf6bbf262e Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 1 Nov 2015 11:19:35 +0000 Subject: [PATCH 210/216] Revert "Quickfix for misterious abstract-class-little-used" This reverts commit 01bc073111d139183e24c9d702d5942beb864022. --- acme/acme/jose/jwk.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/acme/acme/jose/jwk.py b/acme/acme/jose/jwk.py index c82134da9..4d07229b3 100644 --- a/acme/acme/jose/jwk.py +++ b/acme/acme/jose/jwk.py @@ -21,12 +21,6 @@ from acme.jose import util logger = logging.getLogger(__name__) -# TODO: bug in pylint? -# ************* Module acme.challenges -# R:153, 0: Abstract class is only referenced 1 times (abstract-class-little-used) -# pylint: disable=abstract-class-little-used - - class JWK(json_util.TypedJSONObjectWithFields): # pylint: disable=too-few-public-methods """JSON Web Key.""" From ee68d5eb8607edfd7628e0426047fc072399c7a7 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 1 Nov 2015 11:23:56 +0000 Subject: [PATCH 211/216] Another attempt at abstract-class-little-used --- .pylintrc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.pylintrc b/.pylintrc index 268d61ec6..f31b77e9a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -38,8 +38,9 @@ load-plugins=linter_plugin # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=fixme,locally-disabled,abstract-class-not-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name -# abstract-class-not-used cannot be disabled locally (at least in pylint 1.4.1) +disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name +# abstract-class-not-used cannot be disabled locally (at least in +# pylint 1.4.1), same for abstract-class-little-used [REPORTS] From 21bfe5b98329be348a15cfd97b93f21f7aef26c6 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 1 Nov 2015 22:48:58 -0800 Subject: [PATCH 212/216] Remove stray variable --- letsencrypt/tests/cli_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index e38448249..7f8fbb351 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -68,7 +68,6 @@ class CLITest(unittest.TestCase): "Run a help command, and return the help string for scrutiny" output = StringIO.StringIO() with mock.patch('letsencrypt.cli.sys.stdout', new=output): - plugins = disco.PluginsRegistry.find_all() self.assertRaises(SystemExit, self._call_stdout, args) out = output.getvalue() return out From eb09190656e1eb61aca88a047ee0432533abf50f Mon Sep 17 00:00:00 2001 From: Dev & Sec Date: Mon, 2 Nov 2015 21:21:42 +0000 Subject: [PATCH 213/216] Remove duplicated python-devel in redhat bootstrap script --- bootstrap/_rpm_common.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index 3fd0f59f9..26b91b8c4 100755 --- a/bootstrap/_rpm_common.sh +++ b/bootstrap/_rpm_common.sh @@ -21,7 +21,6 @@ $tool install -y \ python \ python-devel \ python-virtualenv \ - python-devel \ gcc \ dialog \ augeas-libs \ From 353240470527f5716df2887dadb9d45bf86e5f31 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 2 Nov 2015 17:18:44 -0800 Subject: [PATCH 214/216] Nit fix --- acme/acme/challenges.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acme/acme/challenges.py b/acme/acme/challenges.py index b424071a1..c5855a7ca 100644 --- a/acme/acme/challenges.py +++ b/acme/acme/challenges.py @@ -282,7 +282,7 @@ class HTTP01(KeyAuthorizationChallenge): typ = response_cls.typ CONTENT_TYPE = "text/plain" - """Content-Type header that must be used for provisioned resource.""" + """Only valid value for Content-Type if the header is included.""" URI_ROOT_PATH = ".well-known/acme-challenge" """URI root path for the server provisioned resource.""" From b11e556ae7ba931a9593b0a82623c662eae8c737 Mon Sep 17 00:00:00 2001 From: Nav Date: Tue, 3 Nov 2015 23:53:36 +0200 Subject: [PATCH 215/216] Removing stray debug statements, fixes #1308 --- letsencrypt/cli.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 39c19346b..9f532f002 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -549,7 +549,6 @@ def plugins_cmd(args, config, plugins): # TODO: Use IDisplay rather than print logger.debug("Filtered plugins: %r", filtered) if not args.init and not args.prepare: - print str(filtered) return filtered.init(config) @@ -557,13 +556,11 @@ def plugins_cmd(args, config, plugins): # TODO: Use IDisplay rather than print logger.debug("Verified plugins: %r", verified) if not args.prepare: - print str(verified) return verified.prepare() available = verified.available() logger.debug("Prepared plugins: %s", available) - print str(available) def read_file(filename, mode="rb"): @@ -935,7 +932,6 @@ def _paths_parser(helpful): section = "paths" if verb in ("install", "revoke"): section = verb - print helpful.help_arg, helpful.help_arg == "install" # revoke --key-path reads a file, install --key-path takes a string add(section, "--key-path", type=((verb == "revoke" and read_file) or str), required=(verb == "install"), From 3d568aceeb847f2b36ed0ec13c7ead9ebc26fd44 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 4 Nov 2015 07:27:30 +0000 Subject: [PATCH 216/216] CRLF: unix2dos docs/make.bat docs/make.bat was using LF, but since e9a2180d16bf8efef4795ccd25ef81f58f2f184f was introduced, this causes headaches, e.g. when releasing. --- docs/make.bat | 526 +++++++++++++++++++++++++------------------------- 1 file changed, 263 insertions(+), 263 deletions(-) diff --git a/docs/make.bat b/docs/make.bat index e83ba8cb5..198e864c3 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -1,263 +1,263 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -set I18NSPHINXOPTS=%SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - echo. coverage to run coverage check of the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -REM Check if sphinx-build is available and fallback to Python version if any -%SPHINXBUILD% 2> nul -if errorlevel 9009 goto sphinx_python -goto sphinx_ok - -:sphinx_python - -set SPHINXBUILD=python -m sphinx.__init__ -%SPHINXBUILD% 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 -) - -:sphinx_ok - - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\LetsEncrypt.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\LetsEncrypt.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "coverage" ( - %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage - if errorlevel 1 exit /b 1 - echo. - echo.Testing of coverage in the sources finished, look at the ^ -results in %BUILDDIR%/coverage/python.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 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 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\LetsEncrypt.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\LetsEncrypt.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end