From c235803bd4d9b07b04cc75c946edaeddc116ec9f Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Mon, 1 Feb 2016 12:32:08 +0200 Subject: [PATCH 001/317] Adding --allow-subset-of-names flag --- letsencrypt/cli.py | 4 ++++ letsencrypt/client.py | 2 +- letsencrypt/tests/client_test.py | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index d5811538e..734fc9d51 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1603,6 +1603,10 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "security", "--strict-permissions", action="store_true", help="Require that all configuration files are owned by the current " "user; only needed if your config is somewhere unsafe like /tmp/") + helpful.add( + "automation", "--allow-subset-of-names", dest="allow_subset_of_names", + action="store_true", default=False, + help="Allow subsets of domain names to fail validation without exiting.") helpful.add_group( "renew", description="The 'renew' subcommand will attempt to renew all" diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 9dfa70e8d..06a09257a 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -222,7 +222,7 @@ class Client(object): logger.debug("CSR: %s, domains: %s", csr, domains) - authzr = self.auth_handler.get_authorizations(domains) + authzr = self.auth_handler.get_authorizations(domains, self.config.allow_subset_of_names) certr = self.acme.request_issuance( jose.ComparableX509( OpenSSL.crypto.load_certificate_request(typ, csr.data)), diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index dbc57565e..7a42cd4a3 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -79,7 +79,7 @@ class ClientTest(unittest.TestCase): def setUp(self): self.config = mock.MagicMock( - no_verify_ssl=False, config_dir="/etc/letsencrypt") + no_verify_ssl=False, config_dir="/etc/letsencrypt", allow_subset_of_names=False) # pylint: disable=star-args self.account = mock.MagicMock(**{"key.pem": KEY}) self.eg_domains = ["example.com", "www.example.com"] @@ -102,7 +102,9 @@ class ClientTest(unittest.TestCase): self.acme.fetch_chain.return_value = mock.sentinel.chain def _check_obtain_certificate(self): - self.client.auth_handler.get_authorizations.assert_called_once_with(self.eg_domains) + self.client.auth_handler.get_authorizations.assert_called_once_with( + self.eg_domains, + self.config.allow_subset_of_names) self.acme.request_issuance.assert_called_once_with( jose.ComparableX509(OpenSSL.crypto.load_certificate_request( OpenSSL.crypto.FILETYPE_ASN1, CSR_SAN)), From b68c5ace0c50a16af482be8e901102c5d7a3bfdc Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Wed, 3 Feb 2016 11:54:39 +0200 Subject: [PATCH 002/317] Creating CSR after auth --- letsencrypt/auth_handler.py | 32 +++++++++++++++++++++++++------- letsencrypt/client.py | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index ffbd70ced..94cf8639c 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -42,6 +42,7 @@ class AuthHandler(object): form of :class:`letsencrypt.achallenges.AnnotatedChallenge` """ + def __init__(self, dv_auth, cont_auth, acme, account): self.dv_auth = dv_auth self.cont_auth = cont_auth @@ -75,19 +76,32 @@ class AuthHandler(object): self._choose_challenges(domains) + failed_domains = set() + # While there are still challenges remaining... while self.dv_c or self.cont_c: cont_resp, dv_resp = self._solve_challenges() logger.info("Waiting for verification...") # Send all Responses - this modifies dv_c and cont_c - self._respond(cont_resp, dv_resp, best_effort) + response = self._respond(cont_resp, dv_resp, best_effort) + + if response: + failed_domains = failed_domains.union(response) + + my_authzr = self.authzr + + returnDomains = set() + #Remove failing domains if best_effort is true + for domain in domains: + if not domain in failed_domains: + returnDomains.add(domain) # Just make sure all decisions are complete. self.verify_authzr_complete() # Only return valid authorizations - return [authzr for authzr in self.authzr.values() - if authzr.body.status == messages.STATUS_VALID] + return ([authzr for authzr in my_authzr.values() + if authzr.body.status == messages.STATUS_VALID], returnDomains) def _choose_challenges(self, domains): """Retrieve necessary challenges to satisfy server.""" @@ -139,11 +153,13 @@ class AuthHandler(object): # Check for updated status... try: - self._poll_challenges(chall_update, best_effort) + result = self._poll_challenges(chall_update, best_effort) finally: # This removes challenges from self.dv_c and self.cont_c self._cleanup_challenges(active_achalls) + return result + def _send_responses(self, achalls, resps, chall_update): """Send responses and make sure errors are handled. @@ -175,6 +191,7 @@ class AuthHandler(object): """Wait for all challenge results to be determined.""" dom_to_check = set(chall_update.keys()) comp_domains = set() + failed_domains = set() rounds = 0 while dom_to_check and rounds < max_rounds: @@ -192,9 +209,8 @@ class AuthHandler(object): chall_update[domain].remove(achall) # We failed some challenges... damage control else: - # Right now... just assume a loss and carry on... if best_effort: - comp_domains.add(domain) + failed_domains.add(domain) else: all_failed_achalls.update( updated for _, updated in failed_achalls) @@ -203,10 +219,12 @@ class AuthHandler(object): _report_failed_challs(all_failed_achalls) raise errors.FailedChallenges(all_failed_achalls) - dom_to_check -= comp_domains + dom_to_check -= comp_domains.union(failed_domains) comp_domains.clear() rounds += 1 + return failed_domains + def _handle_check(self, domain, achalls): """Returns tuple of ('completed', 'failed').""" completed = [] diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 06a09257a..928249caa 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -195,7 +195,7 @@ class Client(object): else: self.auth_handler = None - def obtain_certificate_from_csr(self, domains, csr, + def _obtain_certificate(self, domains, csr, authzr, typ=OpenSSL.crypto.FILETYPE_ASN1): """Obtain certificate. @@ -222,13 +222,35 @@ class Client(object): logger.debug("CSR: %s, domains: %s", csr, domains) - authzr = self.auth_handler.get_authorizations(domains, self.config.allow_subset_of_names) certr = self.acme.request_issuance( jose.ComparableX509( OpenSSL.crypto.load_certificate_request(typ, csr.data)), authzr) return certr, self.acme.fetch_chain(certr) +<<<<<<< HEAD +======= + def obtain_certificate_from_csr(self, csr): + """Obtain certficiate from CSR. + + :param .le_util.CSR csr: DER-encoded Certificate Signing + Request. + + :returns: `.CertificateResource` and certificate chain (as + returned by `.fetch_chain`). + :rtype: tuple + + """ + domains = crypto_util.get_sans_from_csr( + csr.data, OpenSSL.crypto.FILETYPE_ASN1) + + authzr, domains = self.auth_handler.get_authorizations(domains, + self.config.allow_subset_of_names) + + return self._obtain_certificate( + # TODO: add CN to domains? + domains, csr, authzr) +>>>>>>> Creating CSR after auth def obtain_certificate(self, domains): """Obtains a certificate from the ACME server. @@ -244,12 +266,19 @@ class Client(object): :rtype: tuple """ + authzr, domains = self.auth_handler.get_authorizations(domains, + self.config.allow_subset_of_names) + # Create CSR from names key = crypto_util.init_save_key( self.config.rsa_key_size, self.config.key_dir) csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir) +<<<<<<< HEAD return self.obtain_certificate_from_csr(domains, csr) + (key, csr) +======= + return self._obtain_certificate(domains, csr, authzr) + (key, csr) +>>>>>>> Creating CSR after auth def obtain_and_enroll_certificate(self, domains): """Obtain and enroll certificate. From fc2c90726113da922919abc176b35b28d78df516 Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Sat, 6 Feb 2016 15:07:27 +0200 Subject: [PATCH 003/317] Fixing tox cover --- letsencrypt/auth_handler.py | 32 +++++++------------------------- letsencrypt/tests/client_test.py | 12 +++++++++++- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 94cf8639c..ffbd70ced 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -42,7 +42,6 @@ class AuthHandler(object): form of :class:`letsencrypt.achallenges.AnnotatedChallenge` """ - def __init__(self, dv_auth, cont_auth, acme, account): self.dv_auth = dv_auth self.cont_auth = cont_auth @@ -76,32 +75,19 @@ class AuthHandler(object): self._choose_challenges(domains) - failed_domains = set() - # While there are still challenges remaining... while self.dv_c or self.cont_c: cont_resp, dv_resp = self._solve_challenges() logger.info("Waiting for verification...") # Send all Responses - this modifies dv_c and cont_c - response = self._respond(cont_resp, dv_resp, best_effort) - - if response: - failed_domains = failed_domains.union(response) - - my_authzr = self.authzr - - returnDomains = set() - #Remove failing domains if best_effort is true - for domain in domains: - if not domain in failed_domains: - returnDomains.add(domain) + self._respond(cont_resp, dv_resp, best_effort) # Just make sure all decisions are complete. self.verify_authzr_complete() # Only return valid authorizations - return ([authzr for authzr in my_authzr.values() - if authzr.body.status == messages.STATUS_VALID], returnDomains) + return [authzr for authzr in self.authzr.values() + if authzr.body.status == messages.STATUS_VALID] def _choose_challenges(self, domains): """Retrieve necessary challenges to satisfy server.""" @@ -153,13 +139,11 @@ class AuthHandler(object): # Check for updated status... try: - result = self._poll_challenges(chall_update, best_effort) + self._poll_challenges(chall_update, best_effort) finally: # This removes challenges from self.dv_c and self.cont_c self._cleanup_challenges(active_achalls) - return result - def _send_responses(self, achalls, resps, chall_update): """Send responses and make sure errors are handled. @@ -191,7 +175,6 @@ class AuthHandler(object): """Wait for all challenge results to be determined.""" dom_to_check = set(chall_update.keys()) comp_domains = set() - failed_domains = set() rounds = 0 while dom_to_check and rounds < max_rounds: @@ -209,8 +192,9 @@ class AuthHandler(object): chall_update[domain].remove(achall) # We failed some challenges... damage control else: + # Right now... just assume a loss and carry on... if best_effort: - failed_domains.add(domain) + comp_domains.add(domain) else: all_failed_achalls.update( updated for _, updated in failed_achalls) @@ -219,12 +203,10 @@ class AuthHandler(object): _report_failed_challs(all_failed_achalls) raise errors.FailedChallenges(all_failed_achalls) - dom_to_check -= comp_domains.union(failed_domains) + dom_to_check -= comp_domains comp_domains.clear() rounds += 1 - return failed_domains - def _handle_check(self, domain, achalls): """Returns tuple of ('completed', 'failed').""" completed = [] diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 7a42cd4a3..395539659 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -98,6 +98,7 @@ class ClientTest(unittest.TestCase): def _mock_obtain_certificate(self): self.client.auth_handler = mock.MagicMock() + self.client.auth_handler.get_authorizations.return_value = (None, None) self.acme.request_issuance.return_value = mock.sentinel.certr self.acme.fetch_chain.return_value = mock.sentinel.chain @@ -105,10 +106,14 @@ class ClientTest(unittest.TestCase): self.client.auth_handler.get_authorizations.assert_called_once_with( self.eg_domains, self.config.allow_subset_of_names) + + authzr, _ = self.client.auth_handler.get_authorizations() + self.acme.request_issuance.assert_called_once_with( jose.ComparableX509(OpenSSL.crypto.load_certificate_request( OpenSSL.crypto.FILETYPE_ASN1, CSR_SAN)), - self.client.auth_handler.get_authorizations()) + authzr) + self.acme.fetch_chain.assert_called_once_with(mock.sentinel.certr) # FIXME move parts of this to test_cli.py... @@ -148,6 +153,11 @@ class ClientTest(unittest.TestCase): mock_crypto_util.init_save_key.return_value = mock.sentinel.key domains = ["example.com", "www.example.com"] + # return_value is essentially set to (None, None) in + # _mock_obtain_certificate(), which breaks this test. + # Thus fixed by the next line. + self.client.auth_handler.get_authorizations.return_value = (None, domains) + self.assertEqual( self.client.obtain_certificate(domains), (mock.sentinel.certr, mock.sentinel.chain, mock.sentinel.key, csr)) From acc4f52745d0f91b51a945dec5fd3444a5bb6b81 Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Tue, 9 Feb 2016 00:49:24 +0200 Subject: [PATCH 004/317] Fixing integration testing --- letsencrypt/auth_handler.py | 31 ++++++++++++++++++++------ letsencrypt/tests/auth_handler_test.py | 4 ++-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index ffbd70ced..4c5d2b869 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -75,19 +75,32 @@ class AuthHandler(object): self._choose_challenges(domains) + failed_domains = set() + # While there are still challenges remaining... while self.dv_c or self.cont_c: cont_resp, dv_resp = self._solve_challenges() logger.info("Waiting for verification...") # Send all Responses - this modifies dv_c and cont_c - self._respond(cont_resp, dv_resp, best_effort) + response = self._respond(cont_resp, dv_resp, best_effort) + + if response: + failed_domains = failed_domains.union(response) + + my_authzr = self.authzr + + returnDomains = [] + #Remove failing domains if best_effort is true + for domain in domains: + if not domain in failed_domains: + returnDomains.append(domain) # Just make sure all decisions are complete. self.verify_authzr_complete() # Only return valid authorizations - return [authzr for authzr in self.authzr.values() - if authzr.body.status == messages.STATUS_VALID] + return ([authzr for authzr in my_authzr.values() + if authzr.body.status == messages.STATUS_VALID], returnDomains) def _choose_challenges(self, domains): """Retrieve necessary challenges to satisfy server.""" @@ -139,11 +152,13 @@ class AuthHandler(object): # Check for updated status... try: - self._poll_challenges(chall_update, best_effort) + result = self._poll_challenges(chall_update, best_effort) finally: # This removes challenges from self.dv_c and self.cont_c self._cleanup_challenges(active_achalls) + return result + def _send_responses(self, achalls, resps, chall_update): """Send responses and make sure errors are handled. @@ -175,6 +190,7 @@ class AuthHandler(object): """Wait for all challenge results to be determined.""" dom_to_check = set(chall_update.keys()) comp_domains = set() + failed_domains = set() rounds = 0 while dom_to_check and rounds < max_rounds: @@ -192,9 +208,8 @@ class AuthHandler(object): chall_update[domain].remove(achall) # We failed some challenges... damage control else: - # Right now... just assume a loss and carry on... if best_effort: - comp_domains.add(domain) + failed_domains.add(domain) else: all_failed_achalls.update( updated for _, updated in failed_achalls) @@ -203,10 +218,12 @@ class AuthHandler(object): _report_failed_challs(all_failed_achalls) raise errors.FailedChallenges(all_failed_achalls) - dom_to_check -= comp_domains + dom_to_check -= comp_domains.union(failed_domains) comp_domains.clear() rounds += 1 + return failed_domains + def _handle_check(self, domain, achalls): """Returns tuple of ('completed', 'failed').""" completed = [] diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 5a6199ca3..ad658353f 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -96,7 +96,7 @@ class GetAuthorizationsTest(unittest.TestCase): mock_poll.side_effect = self._validate_all - authzr = self.handler.get_authorizations(["0"]) + authzr, domains = self.handler.get_authorizations(["0"]) self.assertEqual(self.mock_net.answer_challenge.call_count, 1) @@ -120,7 +120,7 @@ class GetAuthorizationsTest(unittest.TestCase): mock_poll.side_effect = self._validate_all - authzr = self.handler.get_authorizations(["0", "1", "2"]) + authzr, domains = self.handler.get_authorizations(["0", "1", "2"]) self.assertEqual(self.mock_net.answer_challenge.call_count, 6) From 4f53476cf012d8588408f1f67bf8c3e769204989 Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Tue, 9 Feb 2016 02:04:02 +0200 Subject: [PATCH 005/317] Fixing lint --- letsencrypt/tests/auth_handler_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index ad658353f..313939a97 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -96,7 +96,7 @@ class GetAuthorizationsTest(unittest.TestCase): mock_poll.side_effect = self._validate_all - authzr, domains = self.handler.get_authorizations(["0"]) + authzr, _ = self.handler.get_authorizations(["0"]) self.assertEqual(self.mock_net.answer_challenge.call_count, 1) @@ -120,7 +120,7 @@ class GetAuthorizationsTest(unittest.TestCase): mock_poll.side_effect = self._validate_all - authzr, domains = self.handler.get_authorizations(["0", "1", "2"]) + authzr, _ = self.handler.get_authorizations(["0", "1", "2"]) self.assertEqual(self.mock_net.answer_challenge.call_count, 6) From 7b2c2d3a482177ef3560dddb03355a28813d4686 Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Fri, 12 Feb 2016 14:36:12 +0200 Subject: [PATCH 006/317] Fixing more conflicts --- letsencrypt/client.py | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 928249caa..dad5b87ff 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -195,7 +195,7 @@ class Client(object): else: self.auth_handler = None - def _obtain_certificate(self, domains, csr, authzr, + def obtain_certificate_from_csr(self, domains, csr, authzr, typ=OpenSSL.crypto.FILETYPE_ASN1): """Obtain certificate. @@ -228,30 +228,6 @@ class Client(object): authzr) return certr, self.acme.fetch_chain(certr) -<<<<<<< HEAD -======= - def obtain_certificate_from_csr(self, csr): - """Obtain certficiate from CSR. - - :param .le_util.CSR csr: DER-encoded Certificate Signing - Request. - - :returns: `.CertificateResource` and certificate chain (as - returned by `.fetch_chain`). - :rtype: tuple - - """ - domains = crypto_util.get_sans_from_csr( - csr.data, OpenSSL.crypto.FILETYPE_ASN1) - - authzr, domains = self.auth_handler.get_authorizations(domains, - self.config.allow_subset_of_names) - - return self._obtain_certificate( - # TODO: add CN to domains? - domains, csr, authzr) ->>>>>>> Creating CSR after auth - def obtain_certificate(self, domains): """Obtains a certificate from the ACME server. @@ -274,11 +250,7 @@ class Client(object): self.config.rsa_key_size, self.config.key_dir) csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir) -<<<<<<< HEAD - return self.obtain_certificate_from_csr(domains, csr) + (key, csr) -======= - return self._obtain_certificate(domains, csr, authzr) + (key, csr) ->>>>>>> Creating CSR after auth + return self.obtain_certificate_from_csr(domains, csr, authzr) + (key, csr) def obtain_and_enroll_certificate(self, domains): """Obtain and enroll certificate. From d38828751f7eeb24039f4880ef0512e9c68baf4a Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Fri, 12 Feb 2016 14:45:19 +0200 Subject: [PATCH 007/317] Fixing tests --- letsencrypt/auth_handler.py | 2 ++ letsencrypt/cli.py | 2 +- letsencrypt/client.py | 9 +++++++-- letsencrypt/tests/client_test.py | 7 ++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 4c5d2b869..9afd4f324 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -90,6 +90,8 @@ class AuthHandler(object): my_authzr = self.authzr + logger.debug("authzr: %s", my_authzr) + returnDomains = [] #Remove failing domains if best_effort is true for domain in domains: diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 734fc9d51..49b0e53ff 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -694,7 +694,7 @@ def obtain_cert(config, plugins, lineage=None): if config.csr is not None: assert lineage is None, "Did not expect a CSR with a RenewableCert" csr, typ = config.actual_csr - certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) + certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, False, typ) if config.dry_run: logger.info( "Dry run: skipping saving certificate to %s", config.cert_path) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index dad5b87ff..c9b094910 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -195,7 +195,7 @@ class Client(object): else: self.auth_handler = None - def obtain_certificate_from_csr(self, domains, csr, authzr, + def obtain_certificate_from_csr(self, domains, csr, authzr=False, typ=OpenSSL.crypto.FILETYPE_ASN1): """Obtain certificate. @@ -222,10 +222,15 @@ class Client(object): logger.debug("CSR: %s, domains: %s", csr, domains) + if authzr is False: + authzr, _ = self.auth_handler.get_authorizations( + domains, + self.config.allow_subset_of_names) + certr = self.acme.request_issuance( jose.ComparableX509( OpenSSL.crypto.load_certificate_request(typ, csr.data)), - authzr) + authzr) return certr, self.acme.fetch_chain(certr) def obtain_certificate(self, domains): diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 395539659..220f60a38 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -137,9 +137,14 @@ class ClientTest(unittest.TestCase): self.assertRaises(errors.ConfigurationError, cli.HelpfulArgumentParser.handle_csr, mock_parser, mock_parsed_args) + authzr, _ = self.client.auth_handler.get_authorizations(self.eg_domains, False) + self.assertEqual( (mock.sentinel.certr, mock.sentinel.chain), - self.client.obtain_certificate_from_csr(self.eg_domains, test_csr)) + self.client.obtain_certificate_from_csr( + self.eg_domains, + test_csr, + authzr)) # and that the cert was obtained correctly self._check_obtain_certificate() From d48c560df13f283c83217d04bb09f81de042e48b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6gl?= Date: Sun, 14 Feb 2016 22:21:25 +0100 Subject: [PATCH 008/317] correctly handle IPv6 and IPv4 addresses fix #1143 This commit correctly splits IPv6 addresses into the host and port parts. This will work for normal IPv4 and IPv6 addresses appended by a port number as well es for IPv6 addressess without a port, which should be the normal IPv6 usage. --- letsencrypt/plugins/common.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 37738f5c0..187d84daf 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -110,8 +110,17 @@ class Addr(object): @classmethod def fromstring(cls, str_addr): """Initialize Addr from string.""" - tup = str_addr.partition(':') - return cls((tup[0], tup[2])) + if str_addr.startswith('['): + # ipv6 addresses starts with [ + endIndex = str_addr.rfind(']') + host = str_addr[:endIndex + 1] + port = '' + if len(str_addr) > endIndex + 3 and str_addr[endIndex + 2] == ':': + port = str_addr[endIndex + 3:] + return cls((host, port)) + else: + tup = str_addr.partition(':') + return cls((tup[0], tup[2])) def __str__(self): if self.tup[1]: From b5bd330bd9322310b81856171b5fb0b4ec989be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6gl?= Date: Sun, 14 Feb 2016 23:08:58 +0100 Subject: [PATCH 009/317] add vhost test cases containing IPv6 addresses The two added vhost configuration files should test, whether a reversed order, i.e. first an IPv6 entry followed by an IPv4 one, or an IPv6 adress without a given port works correctly. --- .../{failing => passing}/ipv6-1143.conf | 0 .../{failing => passing}/ipv6-1143b.conf | 0 .../tests/apache-conf-files/passing/ipv6-1143c.conf | 9 +++++++++ .../tests/apache-conf-files/passing/ipv6-1143d.conf | 9 +++++++++ 4 files changed, 18 insertions(+) rename letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/{failing => passing}/ipv6-1143.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/{failing => passing}/ipv6-1143b.conf (100%) create mode 100644 letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf create mode 100644 letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143b.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/ipv6-1143b.conf rename to letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf new file mode 100644 index 000000000..f75dd7850 --- /dev/null +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf @@ -0,0 +1,9 @@ + +DocumentRoot /xxxx/ +ServerName noodles.net.nz +ServerAlias www.noodles.net.nz +CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined + + AllowOverride All + + diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf new file mode 100644 index 000000000..f16b412da --- /dev/null +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf @@ -0,0 +1,9 @@ + +DocumentRoot /xxxx/ +ServerName noodles.net.nz +ServerAlias www.noodles.net.nz +CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined + + AllowOverride All + + From d2a96efa8efb77e9d5423254d8e01bc6a634a112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6gl?= Date: Fri, 19 Feb 2016 23:39:22 +0100 Subject: [PATCH 010/317] add test cases --- letsencrypt/plugins/common_test.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/letsencrypt/plugins/common_test.py b/letsencrypt/plugins/common_test.py index 55319f0a0..5fdd57c5f 100644 --- a/letsencrypt/plugins/common_test.py +++ b/letsencrypt/plugins/common_test.py @@ -81,6 +81,9 @@ class AddrTest(unittest.TestCase): self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:*") self.addr3 = Addr.fromstring("192.168.1.1:80") + self.addr4 = Addr.fromstring("[fe00::1]") + self.addr5 = Addr.fromstring("[fe00::1]:*") + self.addr6 = Addr.fromstring("[fe00::1]:80") def test_fromstring(self): self.assertEqual(self.addr1.get_addr(), "192.168.1.1") @@ -89,22 +92,38 @@ class AddrTest(unittest.TestCase): self.assertEqual(self.addr2.get_port(), "*") self.assertEqual(self.addr3.get_addr(), "192.168.1.1") self.assertEqual(self.addr3.get_port(), "80") + self.assertEqual(self.addr4.get_addr(), "[fe00::1]") + self.assertEqual(self.addr4.get_port(), "") + self.assertEqual(self.addr5.get_addr(), "[fe00::1]") + self.assertEqual(self.addr5.get_port(), "*") + self.assertEqual(self.addr6.get_addr(), "[fe00::1]") + self.assertEqual(self.addr6.get_port(), "80") def test_str(self): self.assertEqual(str(self.addr1), "192.168.1.1") self.assertEqual(str(self.addr2), "192.168.1.1:*") self.assertEqual(str(self.addr3), "192.168.1.1:80") + self.assertEqual(str(self.addr4), "[fe00::1]") + self.assertEqual(str(self.addr5), "[fe00::1]:*") + self.assertEqual(str(self.addr6), "[fe00::1]:80") def test_get_addr_obj(self): self.assertEqual(str(self.addr1.get_addr_obj("443")), "192.168.1.1:443") self.assertEqual(str(self.addr2.get_addr_obj("")), "192.168.1.1") self.assertEqual(str(self.addr1.get_addr_obj("*")), "192.168.1.1:*") + self.assertEqual(str(self.addr4.get_addr_obj("443")), "[fe00::1]:443") + self.assertEqual(str(self.addr5.get_addr_obj("")), "[fe00::1]") + self.assertEqual(str(self.addr4.get_addr_obj("*")), "[fe00::1]:*") def test_eq(self): self.assertEqual(self.addr1, self.addr2.get_addr_obj("")) self.assertNotEqual(self.addr1, self.addr2) self.assertFalse(self.addr1 == 3333) + self.assertEqual(self.addr4, self.addr4.get_addr_obj("")) + self.assertNotEqual(self.addr4, self.addr5) + self.assertFalse(self.addr4 == 3333) + def test_set_inclusion(self): from letsencrypt.plugins.common import Addr set_a = set([self.addr1, self.addr2]) @@ -114,6 +133,13 @@ class AddrTest(unittest.TestCase): self.assertEqual(set_a, set_b) + set_c = set([self.addr4, self.addr5]) + addr4b = Addr.fromstring("[fe00::1]") + addr5b = Addr.fromstring("[fe00::1]:*") + set_d = set([addr4b, addr5b]) + + self.assertEqual(set_c, set_d) + class TLSSNI01Test(unittest.TestCase): """Tests for letsencrypt.plugins.common.TLSSNI01.""" From 9b08fd3964e17ca94353f4757946533d41d5edd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20B=C3=B6gl?= Date: Fri, 19 Feb 2016 23:40:44 +0100 Subject: [PATCH 011/317] correctly parse ipv6 address This commit fixes the wrong used indexes for parsing the ipv6 address. --- letsencrypt/plugins/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 187d84daf..5a8effc2b 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -115,8 +115,8 @@ class Addr(object): endIndex = str_addr.rfind(']') host = str_addr[:endIndex + 1] port = '' - if len(str_addr) > endIndex + 3 and str_addr[endIndex + 2] == ':': - port = str_addr[endIndex + 3:] + if len(str_addr) > endIndex + 2 and str_addr[endIndex + 1] == ':': + port = str_addr[endIndex + 2:] return cls((host, port)) else: tup = str_addr.partition(':') From de31ece45a0d1b9105b85f1871d528c0842949d8 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Sat, 27 Feb 2016 01:03:15 +0200 Subject: [PATCH 012/317] Fixing styling and naming issues --- letsencrypt/auth_handler.py | 19 ++++++------------- letsencrypt/cli.py | 5 +++-- letsencrypt/client.py | 6 +++--- letsencrypt/tests/client_test.py | 2 +- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 9afd4f324..63426f7bd 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -88,21 +88,14 @@ class AuthHandler(object): if response: failed_domains = failed_domains.union(response) - my_authzr = self.authzr - - logger.debug("authzr: %s", my_authzr) - - returnDomains = [] - #Remove failing domains if best_effort is true - for domain in domains: - if not domain in failed_domains: - returnDomains.append(domain) + returnDomains = [domain for domain in domains + if domain not in failed_domains] # Just make sure all decisions are complete. self.verify_authzr_complete() # Only return valid authorizations - return ([authzr for authzr in my_authzr.values() - if authzr.body.status == messages.STATUS_VALID], returnDomains) + return [authzr for authzr in self.authzr.values() + if authzr.body.status == messages.STATUS_VALID], returnDomains def _choose_challenges(self, domains): """Retrieve necessary challenges to satisfy server.""" @@ -154,12 +147,12 @@ class AuthHandler(object): # Check for updated status... try: - result = self._poll_challenges(chall_update, best_effort) + failed_domains = self._poll_challenges(chall_update, best_effort) finally: # This removes challenges from self.dv_c and self.cont_c self._cleanup_challenges(active_achalls) - return result + return failed_domains def _send_responses(self, achalls, resps, chall_update): """Send responses and make sure errors are handled. diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 49b0e53ff..eba05055d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -694,7 +694,7 @@ def obtain_cert(config, plugins, lineage=None): if config.csr is not None: assert lineage is None, "Did not expect a CSR with a RenewableCert" csr, typ = config.actual_csr - certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, False, typ) + certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ, authzr=False) if config.dry_run: logger.info( "Dry run: skipping saving certificate to %s", config.cert_path) @@ -1606,7 +1606,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): helpful.add( "automation", "--allow-subset-of-names", dest="allow_subset_of_names", action="store_true", default=False, - help="Allow subsets of domain names to fail validation without exiting.") + help="Allow subsets of domain names in a single lineage to fail " + "validation without exiting.") helpful.add_group( "renew", description="The 'renew' subcommand will attempt to renew all" diff --git a/letsencrypt/client.py b/letsencrypt/client.py index c9b094910..d825f6da7 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -195,8 +195,8 @@ class Client(object): else: self.auth_handler = None - def obtain_certificate_from_csr(self, domains, csr, authzr=False, - typ=OpenSSL.crypto.FILETYPE_ASN1): + def obtain_certificate_from_csr(self, domains, csr, + typ=OpenSSL.crypto.FILETYPE_ASN1, authzr=False): """Obtain certificate. Internal function with precondition that `domains` are @@ -255,7 +255,7 @@ class Client(object): self.config.rsa_key_size, self.config.key_dir) csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir) - return self.obtain_certificate_from_csr(domains, csr, authzr) + (key, csr) + return self.obtain_certificate_from_csr(domains, csr, authzr=authzr) + (key, csr) def obtain_and_enroll_certificate(self, domains): """Obtain and enroll certificate. diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 220f60a38..07f76b64a 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -144,7 +144,7 @@ class ClientTest(unittest.TestCase): self.client.obtain_certificate_from_csr( self.eg_domains, test_csr, - authzr)) + authzr=authzr)) # and that the cert was obtained correctly self._check_obtain_certificate() From e64fd392dc60fb01a37ccdfd9110d115c2b3f7d5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 28 Feb 2016 23:34:44 -0800 Subject: [PATCH 013/317] Rope refactor: cli -> main --- letsencrypt/cli.py | 712 ++-------------------------------- letsencrypt/main.py | 696 +++++++++++++++++++++++++++++++++ letsencrypt/tests/cli_test.py | 30 +- 3 files changed, 736 insertions(+), 702 deletions(-) create mode 100644 letsencrypt/main.py diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 3551d5a10..d308db72e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1,9 +1,5 @@ """Let's Encrypt CLI.""" from __future__ import print_function - -# TODO: Sanity check all input. Be sure to avoid shell code etc... -# pylint: disable=too-many-lines -# (TODO: split this file into main.py and cli.py) import argparse import atexit import copy @@ -23,10 +19,6 @@ import zope.component import zope.interface.exceptions import zope.interface.verify -from acme import jose - -import letsencrypt - from letsencrypt import account from letsencrypt import colored_logging from letsencrypt import configuration @@ -37,6 +29,7 @@ from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt import log +from letsencrypt import main from letsencrypt import reporter from letsencrypt import storage @@ -44,6 +37,15 @@ from letsencrypt.display import util as display_util from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco +# TODO: Sanity check all input. Be sure to avoid shell code etc... +# pylint: disable=too-many-lines +# (TODO: split this file into main.py and cli.py) + + + + + + logger = logging.getLogger(__name__) # Global, to save us from a lot of argument passing within the scope of this module @@ -127,158 +129,7 @@ def usage_strings(plugins): return USAGE % (apache_doc, nginx_doc), SHORT_USAGE -def _find_domains(config, installer): - if not config.domains: - domains = display_ops.choose_names(installer) - # record in config.domains (so that it can be serialised in renewal config files), - # and set webroot_map entries if applicable - for d in domains: - _process_domain(config, d) - else: - domains = config.domains - - if not domains: - raise errors.Error("Please specify --domains, or --installer that " - "will help in domain names autodiscovery") - - return domains - - -def _determine_account(config): - """Determine which account to use. - - In order to make the renewer (configuration de/serialization) happy, - if ``config.account`` is ``None``, it will be updated based on the - user input. Same for ``config.email``. - - :param argparse.Namespace config: CLI arguments - :param letsencrypt.interface.IConfig config: Configuration object - :param .AccountStorage account_storage: Account storage. - - :returns: Account and optionally ACME client API (biproduct of new - registration). - :rtype: `tuple` of `letsencrypt.account.Account` and - `acme.client.Client` - - """ - account_storage = account.AccountFileStorage(config) - acme = None - - if config.account is not None: - acc = account_storage.load(config.account) - else: - accounts = account_storage.find_all() - if len(accounts) > 1: - acc = display_ops.choose_account(accounts) - elif len(accounts) == 1: - acc = accounts[0] - else: # no account registered yet - if config.email is None and not config.register_unsafely_without_email: - config.namespace.email = display_ops.get_email() - - def _tos_cb(regr): - if config.tos: - return True - msg = ("Please read the Terms of Service at {0}. You " - "must agree in order to register with the ACME " - "server at {1}".format( - regr.terms_of_service, config.server)) - obj = zope.component.getUtility(interfaces.IDisplay) - return obj.yesno(msg, "Agree", "Cancel", cli_flag="--agree-tos") - - try: - acc, acme = client.register( - config, account_storage, tos_cb=_tos_cb) - except errors.MissingCommandlineFlag: - raise - except errors.Error as error: - logger.debug(error, exc_info=True) - raise errors.Error( - "Unable to register an account with ACME server") - - config.namespace.account = acc.id - return acc, acme - - -def _init_le_client(config, authenticator, installer): - if authenticator is not None: - # if authenticator was given, then we will need account... - acc, acme = _determine_account(config) - logger.debug("Picked account: %r", acc) - # XXX - #crypto_util.validate_key_csr(acc.key) - else: - acc, acme = None, None - - return client.Client(config, acc, authenticator, installer, acme=acme) - - -def _find_duplicative_certs(config, domains): - """Find existing certs that duplicate the request.""" - - identical_names_cert, subset_names_cert = None, None - - 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()) - - for renewal_file in _renewal_conf_files(cli_config): - try: - candidate_lineage = storage.RenewableCert(renewal_file, cli_config) - except (errors.CertStorageError, IOError): - logger.warning("Renewal conf file %s is broken. Skipping.", renewal_file) - logger.debug("Traceback was:\n%s", traceback.format_exc()) - continue - # TODO: Handle these differently depending on whether they are - # expired or still valid? - candidate_names = set(candidate_lineage.names()) - if candidate_names == set(domains): - identical_names_cert = candidate_lineage - elif candidate_names.issubset(set(domains)): - # This logic finds and returns the largest subset-names cert - # in the case where there are several available. - if subset_names_cert is None: - subset_names_cert = candidate_lineage - elif len(candidate_names) > len(subset_names_cert.names()): - subset_names_cert = candidate_lineage - - return identical_names_cert, subset_names_cert - - -def _treat_as_renewal(config, domains): - """Determine whether there are duplicated names and how to handle - them (renew, reinstall, newcert, or raising an error to stop - the client run if the user chooses to cancel the operation when - prompted). - - :returns: Two-element tuple containing desired new-certificate behavior as - a string token ("reinstall", "renew", or "newcert"), plus either - a RenewableCert instance or None if renewal shouldn't occur. - - :raises .Error: If the user would like to rerun the client again. - - """ - # Considering the possibility that the requested certificate is - # related to an existing certificate. (config.duplicate, which - # is set with --duplicate, skips all of this logic and forces any - # kind of certificate to be obtained with renewal = False.) - if config.duplicate: - return "newcert", None - # TODO: Also address superset case - ident_names_cert, subset_names_cert = _find_duplicative_certs(config, domains) - # XXX ^ schoen is not sure whether that correctly reads the systemwide - # configuration file. - if ident_names_cert is None and subset_names_cert is None: - return "newcert", None - - if ident_names_cert is not None: - return _handle_identical_cert_request(config, ident_names_cert) - elif subset_names_cert is not None: - return _handle_subset_cert_request(config, domains, subset_names_cert) - - -def _should_renew(config, lineage): +def should_renew(config, lineage): "Return true if any of the circumstances for automatic renewal apply." if config.renew_by_default: logger.info("Auto-renewal forced with --force-renewal...") @@ -293,217 +144,6 @@ def _should_renew(config, lineage): return False -def _handle_identical_cert_request(config, cert): - """Figure out what to do if a cert has the same names as a previously obtained one - - :param storage.RenewableCert cert: - - :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal - :rtype: tuple - - """ - if _should_renew(config, cert): - return "renew", cert - if config.reinstall: - # Set with --reinstall, force an identical certificate to be - # reinstalled without further prompting. - return "reinstall", cert - question = ( - "You have an existing certificate that contains exactly the same " - "domains you requested and isn't close to expiry." - "{br}(ref: {0}){br}{br}What would you like to do?" - ).format(cert.configfile.filename, br=os.linesep) - - if config.verb == "run": - keep_opt = "Attempt to reinstall this existing certificate" - elif config.verb == "certonly": - keep_opt = "Keep the existing certificate for now" - choices = [keep_opt, - "Renew & replace the cert (limit ~5 per 7 days)"] - - display = zope.component.getUtility(interfaces.IDisplay) - response = display.menu(question, choices, "OK", "Cancel", default=0) - if response[0] == display_util.CANCEL: - # TODO: Add notification related to command-line options for - # skipping the menu for this case. - raise errors.Error( - "User chose to cancel the operation and may " - "reinvoke the client.") - elif response[1] == 0: - return "reinstall", cert - elif response[1] == 1: - return "renew", cert - else: - assert False, "This is impossible" - - -def _handle_subset_cert_request(config, domains, cert): - """Figure out what to do if a previous cert had a subset of the names now requested - - :param storage.RenewableCert cert: - - :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal - :rtype: tuple - - """ - existing = ", ".join(cert.names()) - question = ( - "You have an existing certificate that contains a portion of " - "the domains you requested (ref: {0}){br}{br}It contains these " - "names: {1}{br}{br}You requested these names for the new " - "certificate: {2}.{br}{br}Do you want to expand and replace this existing " - "certificate with the new certificate?" - ).format(cert.configfile.filename, - existing, - ", ".join(domains), - br=os.linesep) - if config.expand or config.renew_by_default or zope.component.getUtility( - interfaces.IDisplay).yesno(question, "Expand", "Cancel", - cli_flag="--expand (or in some cases, --duplicate)"): - return "renew", cert - else: - reporter_util = zope.component.getUtility(interfaces.IReporter) - reporter_util.add_message( - "To obtain a new certificate that contains these names without " - "replacing your existing certificate for {0}, you must use the " - "--duplicate option.{br}{br}" - "For example:{br}{br}{1} --duplicate {2}".format( - existing, - sys.argv[0], " ".join(sys.argv[1:]), - br=os.linesep - ), - reporter_util.HIGH_PRIORITY) - raise errors.Error( - "User chose to cancel the operation and may " - "reinvoke the client.") - - -def _report_new_cert(cert_path, fullchain_path): - """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) - 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 {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(and_chain, path, expiry)) - reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) - - -def _suggest_donation_if_appropriate(config, action): - """Potentially suggest a donation to support Let's Encrypt.""" - if config.staging or config.verb == "renew": - # --dry-run implies --staging - return - if action not in ["renew", "newcert"]: - return - reporter_util = zope.component.getUtility(interfaces.IReporter) - msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n" - "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" - "Donating to EFF: https://eff.org/donate-le\n\n") - reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) - - -def _report_successful_dry_run(config): - reporter_util = zope.component.getUtility(interfaces.IReporter) - if config.verb != "renew": - reporter_util.add_message("The dry run was successful.", - reporter_util.HIGH_PRIORITY, on_crash=False) - - -def _auth_from_domains(le_client, config, domains, lineage=None): - """Authenticate and enroll certificate.""" - # Note: This can raise errors... caught above us though. This is now - # a three-way case: reinstall (which results in a no-op here because - # although there is a relevant lineage, we don't do anything to it - # inside this function -- we don't obtain a new certificate), renew - # (which results in treating the request as a renewal), or newcert - # (which results in treating the request as a new certificate request). - - # If lineage is specified, use that one instead of looking around for - # a matching one. - if lineage is None: - # This will find a relevant matching lineage that exists - action, lineage = _treat_as_renewal(config, domains) - else: - # Renewal, where we already know the specific lineage we're - # interested in - action = "renew" - - if action == "reinstall": - # The lineage already exists; allow the caller to try installing - # it without getting a new certificate at all. - return lineage, "reinstall" - elif action == "renew": - original_server = lineage.configuration["renewalparams"]["server"] - _avoid_invalidating_lineage(config, lineage, original_server) - # TODO: schoen wishes to reuse key - discussion - # https://github.com/letsencrypt/letsencrypt/pull/777/files#r40498574 - new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) - # TODO: Check whether it worked! <- or make sure errors are thrown (jdk) - if config.dry_run: - logger.info("Dry run: skipping updating lineage at %s", - os.path.dirname(lineage.cert)) - else: - lineage.save_successor( - lineage.latest_common_version(), OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped), - new_key.pem, crypto_util.dump_pyopenssl_chain(new_chain), - configuration.RenewerConfiguration(config.namespace)) - lineage.update_all_links_to(lineage.latest_common_version()) - # TODO: Check return value of save_successor - # TODO: Also update lineage renewal config with any relevant - # configuration values from this attempt? <- Absolutely (jdkasten) - elif action == "newcert": - # TREAT AS NEW REQUEST - lineage = le_client.obtain_and_enroll_certificate(domains) - if lineage is False: - raise errors.Error("Certificate could not be obtained") - - if not config.dry_run and not config.verb == "renew": - _report_new_cert(lineage.cert, lineage.fullchain) - - return lineage, action - - -def _avoid_invalidating_lineage(config, lineage, original_server): - "Do not renew a valid cert with one from a staging server!" - def _is_staging(srv): - return srv == constants.STAGING_URI or "staging" in srv - - # Some lineages may have begun with --staging, but then had production certs - # added to them - latest_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, - open(lineage.cert).read()) - # all our test certs are from happy hacker fake CA, though maybe one day - # we should test more methodically - now_valid = "fake" not in repr(latest_cert.get_issuer()).lower() - - if _is_staging(config.server): - if not _is_staging(original_server) or now_valid: - if not config.break_my_certs: - names = ", ".join(lineage.names()) - raise errors.Error( - "You've asked to renew/replace a seemingly valid certificate with " - "a test certificate (domains: {0}). We will not do that " - "unless you use the --break-my-certs flag!".format(names)) - - def diagnose_configurator_problem(cfg_type, requested, plugins): """ Raise the most helpful error message about a plugin being unavailable @@ -645,103 +285,6 @@ def record_chosen_plugins(config, plugins, auth, inst): cn.installer = plugins.find_init(inst).name if inst else "none" -# TODO: Make run as close to auth + install as possible -# Possible difficulties: config.csr was hacked into auth -def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals - """Obtain a certificate and install.""" - try: - installer, authenticator = choose_configurator_plugins(config, plugins, "run") - except errors.PluginSelectionError as e: - return e.message - - domains = _find_domains(config, installer) - - # TODO: Handle errors from _init_le_client? - le_client = _init_le_client(config, authenticator, installer) - - lineage, action = _auth_from_domains(le_client, config, domains) - - le_client.deploy_certificate( - domains, lineage.privkey, lineage.cert, - lineage.chain, lineage.fullchain) - - le_client.enhance_config(domains, config) - - if len(lineage.available_versions("cert")) == 1: - display_ops.success_installation(domains) - else: - display_ops.success_renewal(domains, action) - - _suggest_donation_if_appropriate(config, action) - - -def obtain_cert(config, plugins, lineage=None): - """Implements "certonly": authenticate & obtain cert, but do not install it.""" - # pylint: disable=too-many-locals - try: - # installers are used in auth mode to determine domain names - installer, authenticator = choose_configurator_plugins(config, plugins, "certonly") - except errors.PluginSelectionError as e: - logger.info("Could not choose appropriate plugin: %s", e) - raise - - # TODO: Handle errors from _init_le_client? - le_client = _init_le_client(config, authenticator, installer) - - action = "newcert" - # This is a special case; cert and chain are simply saved - if config.csr is not None: - assert lineage is None, "Did not expect a CSR with a RenewableCert" - csr, typ = config.actual_csr - certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) - if config.dry_run: - logger.info( - "Dry run: skipping saving certificate to %s", config.cert_path) - else: - cert_path, _, cert_fullchain = le_client.save_certificate( - certr, chain, config.cert_path, config.chain_path, config.fullchain_path) - _report_new_cert(cert_path, cert_fullchain) - else: - domains = _find_domains(config, installer) - _, action = _auth_from_domains(le_client, config, domains, lineage) - - if config.dry_run: - _report_successful_dry_run(config) - elif config.verb == "renew": - if installer is None: - # Tell the user that the server was not restarted. - print("new certificate deployed without reload, fullchain is", - lineage.fullchain) - else: - # In case of a renewal, reload server to pick up new certificate. - # In principle we could have a configuration option to inhibit this - # from happening. - installer.restart() - print("new certificate deployed with reload of", - config.installer, "server; fullchain is", lineage.fullchain) - _suggest_donation_if_appropriate(config, action) - - -def install(config, plugins): - """Install a previously obtained cert in a server.""" - # XXX: Update for renewer/RenewableCert - # FIXME: be consistent about whether errors are raised or returned from - # this function ... - - try: - installer, _ = choose_configurator_plugins(config, plugins, "install") - except errors.PluginSelectionError as e: - return e.message - - domains = _find_domains(config, installer) - le_client = _init_le_client(config, authenticator=None, installer=installer) - assert config.cert_path is not None # required=True in the subparser - le_client.deploy_certificate( - domains, config.key_path, config.cert_path, config.chain_path, - config.fullchain_path) - le_client.enhance_config(domains, config) - - def _set_by_cli(var): """ Return True if a particular config variable has been set by the user @@ -925,7 +468,7 @@ def _reconstitute(config, full_path): try: for d in renewal_candidate.names(): - _process_domain(config, d) + process_domain(config, d) except errors.ConfigurationError as error: logger.warning("Renewal configuration file %s references a cert " "that contains an invalid domain name. The problem " @@ -1012,9 +555,9 @@ def renew(config, unused_plugins): else: # XXX: ensure that each call here replaces the previous one zope.component.provideUtility(lineage_config) - if _should_renew(lineage_config, renewal_candidate): + if should_renew(lineage_config, renewal_candidate): plugins = plugins_disco.PluginsRegistry.find_all() - obtain_cert(lineage_config, plugins, renewal_candidate) + main.obtain_cert(lineage_config, plugins, renewal_candidate) renew_successes.append(renewal_candidate.fullchain) else: renew_skipped.append(renewal_candidate.fullchain) @@ -1036,63 +579,6 @@ def renew(config, unused_plugins): logger.debug("no renewal failures") -def revoke(config, unused_plugins): # TODO: coop with renewal config - """Revoke a previously obtained certificate.""" - # For user-agent construction - config.namespace.installer = config.namespace.authenticator = "none" - if config.key_path is not None: # revocation by cert key - logger.debug("Revoking %s using cert key %s", - config.cert_path[0], config.key_path[0]) - key = jose.JWK.load(config.key_path[1]) - else: # revocation by account key - logger.debug("Revoking %s using Account Key", config.cert_path[0]) - acc, _ = _determine_account(config) - key = acc.key - acme = client.acme_from_config_key(config, key) - cert = crypto_util.pyopenssl_load_certificate(config.cert_path[1])[0] - acme.revoke(jose.ComparableX509(cert)) - - -def rollback(config, plugins): - """Rollback server configuration changes made during install.""" - client.rollback(config.installer, config.checkpoints, config, plugins) - - -def config_changes(config, unused_plugins): - """Show changes made to server config during installation - - View checkpoints and associated configuration changes. - - """ - client.view_config_changes(config) - - -def plugins_cmd(config, plugins): # TODO: Use IDisplay rather than print - """List server software plugins.""" - logger.debug("Expected interfaces: %s", config.ifaces) - - ifaces = [] if config.ifaces is None else config.ifaces - filtered = plugins.visible().ifaces(ifaces) - logger.debug("Filtered plugins: %r", filtered) - - if not config.init and not config.prepare: - print(str(filtered)) - return - - filtered.init(config) - verified = filtered.verify(ifaces) - logger.debug("Verified plugins: %r", verified) - - if not config.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"): """Returns the given file's contents. @@ -1152,10 +638,10 @@ class HelpfulArgumentParser(object): """ # Maps verbs/subcommands to the functions that implement them - VERBS = {"auth": obtain_cert, "certonly": obtain_cert, - "config_changes": config_changes, "everything": run, - "install": install, "plugins": plugins_cmd, "renew": renew, - "revoke": revoke, "rollback": rollback, "run": run} + VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, + "config_changes": main.config_changes, "everything": main.run, + "install": main.install, "plugins": main.plugins_cmd, "renew": renew, + "revoke": main.revoke, "rollback": main.rollback, "run": main.run} # List of topics for which additional help can be provided HELP_TOPICS = ["all", "security", @@ -1247,7 +733,7 @@ class HelpfulArgumentParser(object): def handle_csr(self, parsed_args): """ Process a --csr flag. This needs to happen early enough that the - webroot plugin can know about the calls to _process_domain + webroot plugin can know about the calls to process_domain """ if parsed_args.verb != "certonly": raise errors.Error("Currently, a CSR file may only be specified " @@ -1270,7 +756,7 @@ class HelpfulArgumentParser(object): logger.debug("PEM CSR parse error %s", traceback.format_exc()) raise errors.Error("Failed to parse CSR file: {0}".format(parsed_args.csr[0])) for d in domains: - _process_domain(parsed_args, d) + process_domain(parsed_args, d) for d in domains: sanitised = le_util.enforce_domain_sanity(d) @@ -1802,7 +1288,7 @@ class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstrin args.webroot_path.append(webroot) -def _process_domain(args_or_config, domain_arg, webroot_path=None): +def process_domain(args_or_config, domain_arg, webroot_path=None): """ Process a new -d flag, helping the webroot plugin construct a map of {domain : webrootpath} if -w / --webroot-path is in use @@ -1828,165 +1314,17 @@ class WebrootMapProcessor(argparse.Action): # pylint: disable=missing-docstring def __call__(self, parser, args, webroot_map_arg, option_string=None): webroot_map = json.loads(webroot_map_arg) for domains, webroot_path in webroot_map.iteritems(): - _process_domain(args, domains, [webroot_path]) + process_domain(args, domains, [webroot_path]) class DomainFlagProcessor(argparse.Action): # pylint: disable=missing-docstring def __call__(self, parser, args, domain_arg, option_string=None): - """Just wrap _process_domain in argparseese.""" - _process_domain(args, domain_arg) + """Just wrap process_domain in argparseese.""" + process_domain(args, domain_arg) -def setup_log_file_handler(config, logfile, fmt): - """Setup file debug logging.""" - log_file_path = os.path.join(config.logs_dir, logfile) - handler = logging.handlers.RotatingFileHandler( - log_file_path, maxBytes=2 ** 20, backupCount=10) - # rotate on each invocation, rollover only possible when maxBytes - # is nonzero and backupCount is nonzero, so we set maxBytes as big - # as possible not to overrun in single CLI invocation (1MB). - handler.doRollover() # TODO: creates empty letsencrypt.log.1 file - handler.setLevel(logging.DEBUG) - handler_formatter = logging.Formatter(fmt=fmt) - handler_formatter.converter = time.gmtime # don't use localtime - handler.setFormatter(handler_formatter) - return handler, log_file_path - - -def _cli_log_handler(config, level, fmt): - if config.text_mode or config.noninteractive_mode or config.verb == "renew": - handler = colored_logging.StreamHandler() - handler.setFormatter(logging.Formatter(fmt)) - else: - handler = log.DialogHandler() - # dialog box is small, display as less as possible - handler.setFormatter(logging.Formatter("%(message)s")) - handler.setLevel(level) - return handler - - -def setup_logging(config, cli_handler_factory, logfile): - """Setup logging.""" - fmt = "%(asctime)s:%(levelname)s:%(name)s:%(message)s" - level = -config.verbose_count * 10 - file_handler, log_file_path = setup_log_file_handler( - config, logfile=logfile, fmt=fmt) - cli_handler = cli_handler_factory(config, level, fmt) - - # TODO: use fileConfig? - - root_logger = logging.getLogger() - root_logger.setLevel(logging.DEBUG) # send all records to handlers - root_logger.addHandler(cli_handler) - root_logger.addHandler(file_handler) - - logger.debug("Root logging level set at %d", level) - logger.info("Saving debug log to %s", log_file_path) - - -def _handle_exception(exc_type, exc_value, trace, config): - """Logs exceptions and reports them to the user. - - Config is used to determine how to display exceptions to the user. In - general, if config.debug is True, then the full exception and traceback is - shown to the user, otherwise it is suppressed. If config itself is None, - then the traceback and exception is attempted to be written to a logfile. - If this is successful, the traceback is suppressed, otherwise it is shown - to the user. sys.exit is always called with a nonzero status. - - """ - logger.debug( - "Exiting abnormally:%s%s", - os.linesep, - "".join(traceback.format_exception(exc_type, exc_value, trace))) - - if issubclass(exc_type, Exception) and (config is None or not config.debug): - if config is None: - logfile = "letsencrypt.log" - try: - with open(logfile, "w") as logfd: - traceback.print_exception( - exc_type, exc_value, trace, file=logfd) - except: # pylint: disable=bare-except - sys.exit("".join( - traceback.format_exception(exc_type, exc_value, trace))) - - if issubclass(exc_type, errors.Error): - sys.exit(exc_value) - else: - # Here we're passing a client or ACME error out to the client at the shell - # Tell the user a bit about what happened, without overwhelming - # them with a full traceback - err = traceback.format_exception_only(exc_type, exc_value)[0] - # Typical error from the ACME module: - # acme.messages.Error: urn:acme:error:malformed :: The request message was - # malformed :: Error creating new registration :: Validation of contact - # mailto:none@longrandomstring.biz failed: Server failure at resolver - if (("urn:acme" in err and ":: " in err and - config.verbose_count <= flag_default("verbose_count"))): - # prune ACME error code, we have a human description - _code, _sep, err = err.partition(":: ") - msg = "An unexpected error occurred:\n" + err + "Please see the " - if config is None: - msg += "logfile '{0}' for more details.".format(logfile) - else: - msg += "logfiles in {0} for more details.".format(config.logs_dir) - sys.exit(msg) - else: - sys.exit("".join( - traceback.format_exception(exc_type, exc_value, trace))) - - -def main(cli_args=sys.argv[1:]): - """Command line argument parsing and main script execution.""" - sys.excepthook = functools.partial(_handle_exception, config=None) - plugins = plugins_disco.PluginsRegistry.find_all() - - # note: arg parser internally handles --help (and exits afterwards) - args = prepare_and_parse_args(plugins, cli_args) - config = configuration.NamespaceConfig(args) - zope.component.provideUtility(config) - - # Setup logging ASAP, otherwise "No handlers could be found for - # logger ..." TODO: this should be done before plugins discovery - for directory in config.config_dir, config.work_dir: - le_util.make_or_verify_dir( - directory, constants.CONFIG_DIRS_MODE, os.geteuid(), - "--strict-permissions" in cli_args) - # TODO: logs might contain sensitive data such as contents of the - # private key! #525 - le_util.make_or_verify_dir( - config.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args) - setup_logging(config, _cli_log_handler, logfile='letsencrypt.log') - - logger.debug("letsencrypt version: %s", letsencrypt.__version__) - # do not log `config`, as it contains sensitive data (e.g. revoke --key)! - logger.debug("Arguments: %r", cli_args) - logger.debug("Discovered plugins: %r", plugins) - - sys.excepthook = functools.partial(_handle_exception, config=config) - - # Displayer - if config.noninteractive_mode: - displayer = display_util.NoninteractiveDisplay(sys.stdout) - elif config.text_mode: - displayer = display_util.FileDisplay(sys.stdout) - elif config.verb == "renew": - config.noninteractive_mode = True - displayer = display_util.NoninteractiveDisplay(sys.stdout) - else: - displayer = display_util.NcursesDisplay() - zope.component.provideUtility(displayer) - - # Reporter - report = reporter.Reporter() - zope.component.provideUtility(report) - atexit.register(report.atexit_print_messages) - - return config.func(config, plugins) - if __name__ == "__main__": - err_string = main() + err_string = main.main() if err_string: logger.warn("Exiting with message %s", err_string) sys.exit(err_string) # pragma: no cover diff --git a/letsencrypt/main.py b/letsencrypt/main.py new file mode 100644 index 000000000..89e7a85a3 --- /dev/null +++ b/letsencrypt/main.py @@ -0,0 +1,696 @@ +from __future__ import print_function +import atexit +import functools +import os +import sys +import zope.component + +from letsencrypt import account +from letsencrypt import client +from letsencrypt import cli +from letsencrypt import crypto_util +from letsencrypt import colored_logging +from letsencrypt import configuration +from letsencrypt import constants +from letsencrypt import errors +from letsencrypt import interfaces +from letsencrypt import le_util +from letsencrypt import log +from letsencrypt import reporter +from letsencrypt import storage + +from letsencrypt.cli import choose_configurator_plugins, _renewal_conf_files, should_renew +from letsencrypt.display import util as display_util, ops as display_ops +from letsencrypt.plugins import disco as plugins_disco + +import traceback +import logging.handlers +import time +from acme import jose +import OpenSSL + +logger = logging.getLogger(__name__) + +def _suggest_donation_if_appropriate(config, action): + """Potentially suggest a donation to support Let's Encrypt.""" + if config.staging or config.verb == "renew": + # --dry-run implies --staging + return + if action not in ["renew", "newcert"]: + return + reporter_util = zope.component.getUtility(interfaces.IReporter) + msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n" + "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" + "Donating to EFF: https://eff.org/donate-le\n\n") + reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) + + +def _avoid_invalidating_lineage(config, lineage, original_server): + "Do not renew a valid cert with one from a staging server!" + def _is_staging(srv): + return srv == constants.STAGING_URI or "staging" in srv + + # Some lineages may have begun with --staging, but then had production certs + # added to them + latest_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, + open(lineage.cert).read()) + # all our test certs are from happy hacker fake CA, though maybe one day + # we should test more methodically + now_valid = "fake" not in repr(latest_cert.get_issuer()).lower() + + if _is_staging(config.server): + if not _is_staging(original_server) or now_valid: + if not config.break_my_certs: + names = ", ".join(lineage.names()) + raise errors.Error( + "You've asked to renew/replace a seemingly valid certificate with " + "a test certificate (domains: {0}). We will not do that " + "unless you use the --break-my-certs flag!".format(names)) + + +def _report_successful_dry_run(config): + reporter_util = zope.component.getUtility(interfaces.IReporter) + if config.verb != "renew": + reporter_util.add_message("The dry run was successful.", + reporter_util.HIGH_PRIORITY, on_crash=False) + + +def _auth_from_domains(le_client, config, domains, lineage=None): + """Authenticate and enroll certificate.""" + # Note: This can raise errors... caught above us though. This is now + # a three-way case: reinstall (which results in a no-op here because + # although there is a relevant lineage, we don't do anything to it + # inside this function -- we don't obtain a new certificate), renew + # (which results in treating the request as a renewal), or newcert + # (which results in treating the request as a new certificate request). + + # If lineage is specified, use that one instead of looking around for + # a matching one. + if lineage is None: + # This will find a relevant matching lineage that exists + action, lineage = _treat_as_renewal(config, domains) + else: + # Renewal, where we already know the specific lineage we're + # interested in + action = "renew" + + if action == "reinstall": + # The lineage already exists; allow the caller to try installing + # it without getting a new certificate at all. + return lineage, "reinstall" + elif action == "renew": + original_server = lineage.configuration["renewalparams"]["server"] + _avoid_invalidating_lineage(config, lineage, original_server) + # TODO: schoen wishes to reuse key - discussion + # https://github.com/letsencrypt/letsencrypt/pull/777/files#r40498574 + new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) + # TODO: Check whether it worked! <- or make sure errors are thrown (jdk) + if config.dry_run: + logger.info("Dry run: skipping updating lineage at %s", + os.path.dirname(lineage.cert)) + else: + lineage.save_successor( + lineage.latest_common_version(), OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped), + new_key.pem, crypto_util.dump_pyopenssl_chain(new_chain), + configuration.RenewerConfiguration(config.namespace)) + lineage.update_all_links_to(lineage.latest_common_version()) + # TODO: Check return value of save_successor + # TODO: Also update lineage renewal config with any relevant + # configuration values from this attempt? <- Absolutely (jdkasten) + elif action == "newcert": + # TREAT AS NEW REQUEST + lineage = le_client.obtain_and_enroll_certificate(domains) + if lineage is False: + raise errors.Error("Certificate could not be obtained") + + if not config.dry_run and not config.verb == "renew": + _report_new_cert(lineage.cert, lineage.fullchain) + + return lineage, action + + +def _handle_subset_cert_request(config, domains, cert): + """Figure out what to do if a previous cert had a subset of the names now requested + + :param storage.RenewableCert cert: + + :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal + :rtype: tuple + + """ + existing = ", ".join(cert.names()) + question = ( + "You have an existing certificate that contains a portion of " + "the domains you requested (ref: {0}){br}{br}It contains these " + "names: {1}{br}{br}You requested these names for the new " + "certificate: {2}.{br}{br}Do you want to expand and replace this existing " + "certificate with the new certificate?" + ).format(cert.configfile.filename, + existing, + ", ".join(domains), + br=os.linesep) + if config.expand or config.renew_by_default or zope.component.getUtility( + interfaces.IDisplay).yesno(question, "Expand", "Cancel", + cli_flag="--expand (or in some cases, --duplicate)"): + return "renew", cert + else: + reporter_util = zope.component.getUtility(interfaces.IReporter) + reporter_util.add_message( + "To obtain a new certificate that contains these names without " + "replacing your existing certificate for {0}, you must use the " + "--duplicate option.{br}{br}" + "For example:{br}{br}{1} --duplicate {2}".format( + existing, + sys.argv[0], " ".join(sys.argv[1:]), + br=os.linesep + ), + reporter_util.HIGH_PRIORITY) + raise errors.Error( + "User chose to cancel the operation and may " + "reinvoke the client.") + + +def _handle_identical_cert_request(config, cert): + """Figure out what to do if a cert has the same names as a previously obtained one + + :param storage.RenewableCert cert: + + :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal + :rtype: tuple + + """ + if should_renew(config, cert): + return "renew", cert + if config.reinstall: + # Set with --reinstall, force an identical certificate to be + # reinstalled without further prompting. + return "reinstall", cert + question = ( + "You have an existing certificate that contains exactly the same " + "domains you requested and isn't close to expiry." + "{br}(ref: {0}){br}{br}What would you like to do?" + ).format(cert.configfile.filename, br=os.linesep) + + if config.verb == "run": + keep_opt = "Attempt to reinstall this existing certificate" + elif config.verb == "certonly": + keep_opt = "Keep the existing certificate for now" + choices = [keep_opt, + "Renew & replace the cert (limit ~5 per 7 days)"] + + display = zope.component.getUtility(interfaces.IDisplay) + response = display.menu(question, choices, "OK", "Cancel", default=0) + if response[0] == display_util.CANCEL: + # TODO: Add notification related to command-line options for + # skipping the menu for this case. + raise errors.Error( + "User chose to cancel the operation and may " + "reinvoke the client.") + elif response[1] == 0: + return "reinstall", cert + elif response[1] == 1: + return "renew", cert + else: + assert False, "This is impossible" + + +def _treat_as_renewal(config, domains): + """Determine whether there are duplicated names and how to handle + them (renew, reinstall, newcert, or raising an error to stop + the client run if the user chooses to cancel the operation when + prompted). + + :returns: Two-element tuple containing desired new-certificate behavior as + a string token ("reinstall", "renew", or "newcert"), plus either + a RenewableCert instance or None if renewal shouldn't occur. + + :raises .Error: If the user would like to rerun the client again. + + """ + # Considering the possibility that the requested certificate is + # related to an existing certificate. (config.duplicate, which + # is set with --duplicate, skips all of this logic and forces any + # kind of certificate to be obtained with renewal = False.) + if config.duplicate: + return "newcert", None + # TODO: Also address superset case + ident_names_cert, subset_names_cert = _find_duplicative_certs(config, domains) + # XXX ^ schoen is not sure whether that correctly reads the systemwide + # configuration file. + if ident_names_cert is None and subset_names_cert is None: + return "newcert", None + + if ident_names_cert is not None: + return _handle_identical_cert_request(config, ident_names_cert) + elif subset_names_cert is not None: + return _handle_subset_cert_request(config, domains, subset_names_cert) + + +def _find_duplicative_certs(config, domains): + """Find existing certs that duplicate the request.""" + + identical_names_cert, subset_names_cert = None, None + + 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()) + + for renewal_file in _renewal_conf_files(cli_config): + try: + candidate_lineage = storage.RenewableCert(renewal_file, cli_config) + except (errors.CertStorageError, IOError): + logger.warning("Renewal conf file %s is broken. Skipping.", renewal_file) + logger.debug("Traceback was:\n%s", traceback.format_exc()) + continue + # TODO: Handle these differently depending on whether they are + # expired or still valid? + candidate_names = set(candidate_lineage.names()) + if candidate_names == set(domains): + identical_names_cert = candidate_lineage + elif candidate_names.issubset(set(domains)): + # This logic finds and returns the largest subset-names cert + # in the case where there are several available. + if subset_names_cert is None: + subset_names_cert = candidate_lineage + elif len(candidate_names) > len(subset_names_cert.names()): + subset_names_cert = candidate_lineage + + return identical_names_cert, subset_names_cert + + +def _find_domains(config, installer): + if not config.domains: + domains = display_ops.choose_names(installer) + # record in config.domains (so that it can be serialised in renewal config files), + # and set webroot_map entries if applicable + for d in domains: + cli.process_domain(config, d) + else: + domains = config.domains + + if not domains: + raise errors.Error("Please specify --domains, or --installer that " + "will help in domain names autodiscovery") + + return domains + + +def _report_new_cert(cert_path, fullchain_path): + """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) + 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 {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(and_chain, path, expiry)) + reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) + + +def _determine_account(config): + """Determine which account to use. + + In order to make the renewer (configuration de/serialization) happy, + if ``config.account`` is ``None``, it will be updated based on the + user input. Same for ``config.email``. + + :param argparse.Namespace config: CLI arguments + :param letsencrypt.interface.IConfig config: Configuration object + :param .AccountStorage account_storage: Account storage. + + :returns: Account and optionally ACME client API (biproduct of new + registration). + :rtype: `tuple` of `letsencrypt.account.Account` and + `acme.client.Client` + + """ + account_storage = account.AccountFileStorage(config) + acme = None + + if config.account is not None: + acc = account_storage.load(config.account) + else: + accounts = account_storage.find_all() + if len(accounts) > 1: + acc = display_ops.choose_account(accounts) + elif len(accounts) == 1: + acc = accounts[0] + else: # no account registered yet + if config.email is None and not config.register_unsafely_without_email: + config.namespace.email = display_ops.get_email() + + def _tos_cb(regr): + if config.tos: + return True + msg = ("Please read the Terms of Service at {0}. You " + "must agree in order to register with the ACME " + "server at {1}".format( + regr.terms_of_service, config.server)) + obj = zope.component.getUtility(interfaces.IDisplay) + return obj.yesno(msg, "Agree", "Cancel", cli_flag="--agree-tos") + + try: + acc, acme = client.register( + config, account_storage, tos_cb=_tos_cb) + except errors.MissingCommandlineFlag: + raise + except errors.Error as error: + logger.debug(error, exc_info=True) + raise errors.Error( + "Unable to register an account with ACME server") + + config.namespace.account = acc.id + return acc, acme + + +def _init_le_client(config, authenticator, installer): + if authenticator is not None: + # if authenticator was given, then we will need account... + acc, acme = _determine_account(config) + logger.debug("Picked account: %r", acc) + # XXX + #crypto_util.validate_key_csr(acc.key) + else: + acc, acme = None, None + + return client.Client(config, acc, authenticator, installer, acme=acme) + + +def install(config, plugins): + """Install a previously obtained cert in a server.""" + # XXX: Update for renewer/RenewableCert + # FIXME: be consistent about whether errors are raised or returned from + # this function ... + + try: + installer, _ = choose_configurator_plugins(config, plugins, "install") + except errors.PluginSelectionError as e: + return e.message + + domains = _find_domains(config, installer) + le_client = _init_le_client(config, authenticator=None, installer=installer) + assert config.cert_path is not None # required=True in the subparser + le_client.deploy_certificate( + domains, config.key_path, config.cert_path, config.chain_path, + config.fullchain_path) + le_client.enhance_config(domains, config) + + +def plugins_cmd(config, plugins): # TODO: Use IDisplay rather than print + """List server software plugins.""" + logger.debug("Expected interfaces: %s", config.ifaces) + + ifaces = [] if config.ifaces is None else config.ifaces + filtered = plugins.visible().ifaces(ifaces) + logger.debug("Filtered plugins: %r", filtered) + + if not config.init and not config.prepare: + print(str(filtered)) + return + + filtered.init(config) + verified = filtered.verify(ifaces) + logger.debug("Verified plugins: %r", verified) + + if not config.prepare: + print(str(verified)) + return + + verified.prepare() + available = verified.available() + logger.debug("Prepared plugins: %s", available) + print(str(available)) + + +def rollback(config, plugins): + """Rollback server configuration changes made during install.""" + client.rollback(config.installer, config.checkpoints, config, plugins) + + +def config_changes(config, unused_plugins): + """Show changes made to server config during installation + + View checkpoints and associated configuration changes. + + """ + client.view_config_changes(config) + + +def revoke(config, unused_plugins): # TODO: coop with renewal config + """Revoke a previously obtained certificate.""" + # For user-agent construction + config.namespace.installer = config.namespace.authenticator = "none" + if config.key_path is not None: # revocation by cert key + logger.debug("Revoking %s using cert key %s", + config.cert_path[0], config.key_path[0]) + key = jose.JWK.load(config.key_path[1]) + else: # revocation by account key + logger.debug("Revoking %s using Account Key", config.cert_path[0]) + acc, _ = _determine_account(config) + key = acc.key + acme = client.acme_from_config_key(config, key) + cert = crypto_util.pyopenssl_load_certificate(config.cert_path[1])[0] + acme.revoke(jose.ComparableX509(cert)) + + +def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals + """Obtain a certificate and install.""" + # TODO: Make run as close to auth + install as possible + # Possible difficulties: config.csr was hacked into auth + try: + installer, authenticator = choose_configurator_plugins(config, plugins, "run") + except errors.PluginSelectionError as e: + return e.message + + domains = _find_domains(config, installer) + + # TODO: Handle errors from _init_le_client? + le_client = _init_le_client(config, authenticator, installer) + + lineage, action = _auth_from_domains(le_client, config, domains) + + le_client.deploy_certificate( + domains, lineage.privkey, lineage.cert, + lineage.chain, lineage.fullchain) + + le_client.enhance_config(domains, config) + + if len(lineage.available_versions("cert")) == 1: + display_ops.success_installation(domains) + else: + display_ops.success_renewal(domains, action) + + _suggest_donation_if_appropriate(config, action) + + +def obtain_cert(config, plugins, lineage=None): + """Implements "certonly": authenticate & obtain cert, but do not install it.""" + # pylint: disable=too-many-locals + try: + # installers are used in auth mode to determine domain names + installer, authenticator = choose_configurator_plugins(config, plugins, "certonly") + except errors.PluginSelectionError as e: + logger.info("Could not choose appropriate plugin: %s", e) + raise + + # TODO: Handle errors from _init_le_client? + le_client = _init_le_client(config, authenticator, installer) + + action = "newcert" + # This is a special case; cert and chain are simply saved + if config.csr is not None: + assert lineage is None, "Did not expect a CSR with a RenewableCert" + csr, typ = config.actual_csr + certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) + if config.dry_run: + logger.info( + "Dry run: skipping saving certificate to %s", config.cert_path) + else: + cert_path, _, cert_fullchain = le_client.save_certificate( + certr, chain, config.cert_path, config.chain_path, config.fullchain_path) + _report_new_cert(cert_path, cert_fullchain) + else: + domains = _find_domains(config, installer) + _, action = _auth_from_domains(le_client, config, domains, lineage) + + if config.dry_run: + _report_successful_dry_run(config) + elif config.verb == "renew": + if installer is None: + # Tell the user that the server was not restarted. + print("new certificate deployed without reload, fullchain is", + lineage.fullchain) + else: + # In case of a renewal, reload server to pick up new certificate. + # In principle we could have a configuration option to inhibit this + # from happening. + installer.restart() + print("new certificate deployed with reload of", + config.installer, "server; fullchain is", lineage.fullchain) + _suggest_donation_if_appropriate(config, action) + + +def setup_log_file_handler(config, logfile, fmt): + """Setup file debug logging.""" + log_file_path = os.path.join(config.logs_dir, logfile) + handler = logging.handlers.RotatingFileHandler( + log_file_path, maxBytes=2 ** 20, backupCount=10) + # rotate on each invocation, rollover only possible when maxBytes + # is nonzero and backupCount is nonzero, so we set maxBytes as big + # as possible not to overrun in single CLI invocation (1MB). + handler.doRollover() # TODO: creates empty letsencrypt.log.1 file + handler.setLevel(logging.DEBUG) + handler_formatter = logging.Formatter(fmt=fmt) + handler_formatter.converter = time.gmtime # don't use localtime + handler.setFormatter(handler_formatter) + return handler, log_file_path + + +def _cli_log_handler(config, level, fmt): + if config.text_mode or config.noninteractive_mode or config.verb == "renew": + handler = colored_logging.StreamHandler() + handler.setFormatter(logging.Formatter(fmt)) + else: + handler = log.DialogHandler() + # dialog box is small, display as less as possible + handler.setFormatter(logging.Formatter("%(message)s")) + handler.setLevel(level) + return handler + + +def setup_logging(config, cli_handler_factory, logfile): + """Setup logging.""" + fmt = "%(asctime)s:%(levelname)s:%(name)s:%(message)s" + level = -config.verbose_count * 10 + file_handler, log_file_path = setup_log_file_handler( + config, logfile=logfile, fmt=fmt) + cli_handler = cli_handler_factory(config, level, fmt) + + # TODO: use fileConfig? + + root_logger = logging.getLogger() + root_logger.setLevel(logging.DEBUG) # send all records to handlers + root_logger.addHandler(cli_handler) + root_logger.addHandler(file_handler) + + logger.debug("Root logging level set at %d", level) + logger.info("Saving debug log to %s", log_file_path) + + +def _handle_exception(exc_type, exc_value, trace, config): + """Logs exceptions and reports them to the user. + + Config is used to determine how to display exceptions to the user. In + general, if config.debug is True, then the full exception and traceback is + shown to the user, otherwise it is suppressed. If config itself is None, + then the traceback and exception is attempted to be written to a logfile. + If this is successful, the traceback is suppressed, otherwise it is shown + to the user. sys.exit is always called with a nonzero status. + + """ + logger.debug( + "Exiting abnormally:%s%s", + os.linesep, + "".join(traceback.format_exception(exc_type, exc_value, trace))) + + if issubclass(exc_type, Exception) and (config is None or not config.debug): + if config is None: + logfile = "letsencrypt.log" + try: + with open(logfile, "w") as logfd: + traceback.print_exception( + exc_type, exc_value, trace, file=logfd) + except: # pylint: disable=bare-except + sys.exit("".join( + traceback.format_exception(exc_type, exc_value, trace))) + + if issubclass(exc_type, errors.Error): + sys.exit(exc_value) + else: + # Here we're passing a client or ACME error out to the client at the shell + # Tell the user a bit about what happened, without overwhelming + # them with a full traceback + err = traceback.format_exception_only(exc_type, exc_value)[0] + # Typical error from the ACME module: + # acme.messages.Error: urn:acme:error:malformed :: The request message was + # malformed :: Error creating new registration :: Validation of contact + # mailto:none@longrandomstring.biz failed: Server failure at resolver + if (("urn:acme" in err and ":: " in err and + config.verbose_count <= cli.flag_default("verbose_count"))): + # prune ACME error code, we have a human description + _code, _sep, err = err.partition(":: ") + msg = "An unexpected error occurred:\n" + err + "Please see the " + if config is None: + msg += "logfile '{0}' for more details.".format(logfile) + else: + msg += "logfiles in {0} for more details.".format(config.logs_dir) + sys.exit(msg) + else: + sys.exit("".join( + traceback.format_exception(exc_type, exc_value, trace))) + + +def main(cli_args=sys.argv[1:]): + """Command line argument parsing and main script execution.""" + sys.excepthook = functools.partial(_handle_exception, config=None) + plugins = plugins_disco.PluginsRegistry.find_all() + + # note: arg parser internally handles --help (and exits afterwards) + args = cli.prepare_and_parse_args(plugins, cli_args) + config = configuration.NamespaceConfig(args) + zope.component.provideUtility(config) + + # Setup logging ASAP, otherwise "No handlers could be found for + # logger ..." TODO: this should be done before plugins discovery + for directory in config.config_dir, config.work_dir: + le_util.make_or_verify_dir( + directory, constants.CONFIG_DIRS_MODE, os.geteuid(), + "--strict-permissions" in cli_args) + # TODO: logs might contain sensitive data such as contents of the + # private key! #525 + le_util.make_or_verify_dir( + config.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args) + setup_logging(config, _cli_log_handler, logfile='letsencrypt.log') + + logger.debug("letsencrypt version: %s", letsencrypt.__version__) + # do not log `config`, as it contains sensitive data (e.g. revoke --key)! + logger.debug("Arguments: %r", cli_args) + logger.debug("Discovered plugins: %r", plugins) + + sys.excepthook = functools.partial(_handle_exception, config=config) + + # Displayer + if config.noninteractive_mode: + displayer = display_util.NoninteractiveDisplay(sys.stdout) + elif config.text_mode: + displayer = display_util.FileDisplay(sys.stdout) + elif config.verb == "renew": + config.noninteractive_mode = True + displayer = display_util.NoninteractiveDisplay(sys.stdout) + else: + displayer = display_util.NcursesDisplay() + zope.component.provideUtility(displayer) + + # Reporter + report = reporter.Reporter() + zope.component.provideUtility(report) + atexit.register(report.atexit_print_messages) + + return config.func(config, plugins) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index aef3447c3..f17f0fb73 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -13,7 +13,7 @@ import mock from acme import jose -from letsencrypt import account +from letsencrypt import account, main from letsencrypt import cli from letsencrypt import configuration from letsencrypt import constants @@ -60,7 +60,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args = self.standard_args + args with mock.patch('letsencrypt.cli.sys.stdout') as stdout: with mock.patch('letsencrypt.cli.sys.stderr') as stderr: - ret = cli.main(args[:]) # NOTE: parser can alter its args! + ret = main.main(args[:]) # NOTE: parser can alter its args! return ret, stdout, stderr def _call_stdout(self, args): @@ -71,7 +71,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args = self.standard_args + args with mock.patch('letsencrypt.cli.sys.stderr') as stderr: with mock.patch('letsencrypt.cli.client') as client: - ret = cli.main(args[:]) # NOTE: parser can alter its args! + ret = main.main(args[:]) # NOTE: parser can alter its args! return ret, None, stderr, client def test_no_flags(self): @@ -136,7 +136,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods exc = None try: with mock.patch('letsencrypt.cli.sys.stderr'): - cli.main(self.standard_args + args[:]) # NOTE: parser can alter its args! + main.main(self.standard_args + args[:]) # NOTE: parser can alter its args! except errors.MissingCommandlineFlag as exc: self.assertTrue(message in str(exc)) self.assertTrue(exc is not None) @@ -459,7 +459,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if domains_arg: webroot_map_args.extend(["-d", domains_arg]) namespace = parse(webroot_map_args) - domains = cli._find_domains(namespace, mock.MagicMock()) # pylint: disable=protected-access + domains = main._find_domains(namespace, mock.MagicMock()) # pylint: disable=protected-access self.assertEqual(namespace.webroot_map, expected_map) self.assertEqual(set(domains), set(expectect_domains)) @@ -835,7 +835,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with mock.patch('letsencrypt.cli.open', mock_open, create=True): exception = Exception('detail') config.verbose_count = 1 - cli._handle_exception( + main._handle_exception( Exception, exc_value=exception, trace=None, config=None) mock_open().write.assert_called_once_with(''.join( traceback.format_exception_only(Exception, exception))) @@ -845,7 +845,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with mock.patch('letsencrypt.cli.open', mock_open, create=True): mock_open.side_effect = [KeyboardInterrupt] error = errors.Error('detail') - cli._handle_exception( + main._handle_exception( errors.Error, exc_value=error, trace=None, config=None) # assert_any_call used because sys.exit doesn't exit in cli.py mock_sys.exit.assert_any_call(''.join( @@ -854,7 +854,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods exception = messages.Error(detail='alpha', typ='urn:acme:error:triffid', title='beta') config = mock.MagicMock(debug=False, verbose_count=-3) - cli._handle_exception( + main._handle_exception( messages.Error, exc_value=exception, trace=None, config=config) error_msg = mock_sys.exit.call_args_list[-1][0][0] self.assertTrue('unexpected error' in error_msg) @@ -862,7 +862,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue('alpha' in error_msg) self.assertTrue('beta' in error_msg) config = mock.MagicMock(debug=False, verbose_count=1) - cli._handle_exception( + main._handle_exception( messages.Error, exc_value=exception, trace=None, config=config) error_msg = mock_sys.exit.call_args_list[-1][0][0] self.assertTrue('unexpected error' in error_msg) @@ -870,7 +870,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue('alpha' in error_msg) interrupt = KeyboardInterrupt('detail') - cli._handle_exception( + main._handle_exception( KeyboardInterrupt, exc_value=interrupt, trace=None, config=None) mock_sys.exit.assert_called_with(''.join( traceback.format_exception_only(KeyboardInterrupt, interrupt))) @@ -909,7 +909,7 @@ class DetermineAccountTest(unittest.TestCase): from letsencrypt.cli import _determine_account with mock.patch('letsencrypt.cli.account.AccountFileStorage') as mock_storage: mock_storage.return_value = self.account_storage - return _determine_account(self.config) + return main._determine_account(self.config) def test_args_account_set(self): self.account_storage.save(self.accs[1]) @@ -977,24 +977,24 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): f.write(test_cert) # No overlap at all - result = _find_duplicative_certs( + result = main._find_duplicative_certs( self.cli_config, ['wow.net', 'hooray.org']) self.assertEqual(result, (None, None)) # Totally identical - result = _find_duplicative_certs( + result = main._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( + result = main._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( + result = main._find_duplicative_certs( self.cli_config, ['example.com', 'something.new']) self.assertEqual(result, (None, None)) From 001c1cd8354e6cc8da7cf7cbc205e1021d115e91 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sun, 28 Feb 2016 23:49:11 -0800 Subject: [PATCH 014/317] Refactor cli -> main With some help from rope... --- letsencrypt/cli.py | 28 +++------------------------- letsencrypt/main.py | 9 +++++++++ letsencrypt/tests/cli_test.py | 17 +++++++++-------- 3 files changed, 21 insertions(+), 33 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index d308db72e..df56d74ae 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1,16 +1,13 @@ -"""Let's Encrypt CLI.""" +"""Let's Encrypt command CLI argument processing.""" from __future__ import print_function import argparse -import atexit import copy -import functools import glob import json import logging import logging.handlers import os import sys -import time import traceback import configargparse @@ -19,32 +16,20 @@ import zope.component import zope.interface.exceptions import zope.interface.verify -from letsencrypt import account -from letsencrypt import colored_logging +import letsencrypt + from letsencrypt import configuration from letsencrypt import constants -from letsencrypt import client from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util -from letsencrypt import log from letsencrypt import main -from letsencrypt import reporter from letsencrypt import storage -from letsencrypt.display import util as display_util from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco -# TODO: Sanity check all input. Be sure to avoid shell code etc... -# pylint: disable=too-many-lines -# (TODO: split this file into main.py and cli.py) - - - - - logger = logging.getLogger(__name__) @@ -1321,10 +1306,3 @@ class DomainFlagProcessor(argparse.Action): # pylint: disable=missing-docstring def __call__(self, parser, args, domain_arg, option_string=None): """Just wrap process_domain in argparseese.""" process_domain(args, domain_arg) - - -if __name__ == "__main__": - err_string = main.main() - if err_string: - logger.warn("Exiting with message %s", err_string) - sys.exit(err_string) # pragma: no cover diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 89e7a85a3..516cdf843 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -1,3 +1,4 @@ +"""Let's Encrypt main entry point.""" from __future__ import print_function import atexit import functools @@ -5,6 +6,8 @@ import os import sys import zope.component +import letsencrypt + from letsencrypt import account from letsencrypt import client from letsencrypt import cli @@ -694,3 +697,9 @@ def main(cli_args=sys.argv[1:]): atexit.register(report.atexit_print_messages) return config.func(config, plugins) + +if __name__ == "__main__": + err_string = main() + if err_string: + logger.warn("Exiting with message %s", err_string) + sys.exit(err_string) # pragma: no cover diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index f17f0fb73..c3fd91c11 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -13,13 +13,14 @@ import mock from acme import jose -from letsencrypt import account, main +from letsencrypt import account from letsencrypt import cli from letsencrypt import configuration from letsencrypt import constants from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import le_util +from letsencrypt import main from letsencrypt import storage from letsencrypt.plugins import disco @@ -906,10 +907,10 @@ class DetermineAccountTest(unittest.TestCase): def _call(self): # pylint: disable=protected-access - from letsencrypt.cli import _determine_account + from letsencrypt.main import _determine_account with mock.patch('letsencrypt.cli.account.AccountFileStorage') as mock_storage: mock_storage.return_value = self.account_storage - return main._determine_account(self.config) + return _determine_account(self.config) def test_args_account_set(self): self.account_storage.save(self.accs[1]) @@ -971,30 +972,30 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): @mock.patch('letsencrypt.le_util.make_or_verify_dir') def test_find_duplicative_names(self, unused_makedir): - from letsencrypt.cli import _find_duplicative_certs + from letsencrypt.main import _find_duplicative_certs test_cert = test_util.load_vector('cert-san.pem') with open(self.test_rc.cert, 'w') as f: f.write(test_cert) # No overlap at all - result = main._find_duplicative_certs( + result = _find_duplicative_certs( self.cli_config, ['wow.net', 'hooray.org']) self.assertEqual(result, (None, None)) # Totally identical - result = main._find_duplicative_certs( + 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 = main._find_duplicative_certs( + 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 = main._find_duplicative_certs( + result = _find_duplicative_certs( self.cli_config, ['example.com', 'something.new']) self.assertEqual(result, (None, None)) From 1ae8d344b09938be41b6599b2b0ec69a0f0ff17f Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 10 Mar 2016 17:53:57 -0800 Subject: [PATCH 015/317] Endure incredible amounts of mockery to ensure that tests pass --- letsencrypt/cli.py | 22 ++++---- letsencrypt/main.py | 11 ++++ letsencrypt/tests/cli_test.py | 96 +++++++++++++++++------------------ 3 files changed, 68 insertions(+), 61 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index df56d74ae..5ed97d03f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -24,7 +24,6 @@ from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util -from letsencrypt import main from letsencrypt import storage from letsencrypt.display import ops as display_ops @@ -542,6 +541,7 @@ def renew(config, unused_plugins): zope.component.provideUtility(lineage_config) if should_renew(lineage_config, renewal_candidate): plugins = plugins_disco.PluginsRegistry.find_all() + from letsencrypt import main main.obtain_cert(lineage_config, plugins, renewal_candidate) renew_successes.append(renewal_candidate.fullchain) else: @@ -612,7 +612,6 @@ class SilentParser(object): # pylint: disable=too-few-public-methods kwargs["help"] = argparse.SUPPRESS self.parser.add_argument(*args, **kwargs) - class HelpfulArgumentParser(object): """Argparse Wrapper. @@ -622,19 +621,16 @@ class HelpfulArgumentParser(object): """ - # Maps verbs/subcommands to the functions that implement them - VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, - "config_changes": main.config_changes, "everything": main.run, - "install": main.install, "plugins": main.plugins_cmd, "renew": renew, - "revoke": main.revoke, "rollback": main.rollback, "run": main.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, detect_defaults=False): + from letsencrypt import main + self.VERBS = main.VERBS + + # List of topics for which additional help can be provided + HELP_TOPICS = ["all", "security", + "paths", "automation", "testing"] + main.VERBS.keys() + plugin_names = [name for name, _p in plugins.iteritems()] - self.help_topics = self.HELP_TOPICS + plugin_names + [None] + self.help_topics = HELP_TOPICS + plugin_names + [None] usage, short_usage = usage_strings(plugins) self.parser = configargparse.ArgParser( usage=short_usage, diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 516cdf843..e8b3f7345 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -34,6 +34,7 @@ import OpenSSL logger = logging.getLogger(__name__) + def _suggest_donation_if_appropriate(config, action): """Potentially suggest a donation to support Let's Encrypt.""" if config.staging or config.verb == "renew": @@ -698,6 +699,16 @@ def main(cli_args=sys.argv[1:]): return config.func(config, plugins) + +# Maps verbs/subcommands to the functions that implement them +# In principle this should live in cli.HelpfulArgumentParser, but +# due to issues with import cycles and testing, it lives here +VERBS = {"auth": obtain_cert, "certonly": obtain_cert, + "config_changes": config_changes, "everything": run, + "install": install, "plugins": plugins_cmd, "renew": cli.renew, + "revoke": revoke, "rollback": rollback, "run": run} + + if __name__ == "__main__": err_string = main() if err_string: diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index c3fd91c11..282a0b1d3 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -52,15 +52,15 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _call(self, args): "Run the cli with output streams and actual client mocked out" - with mock.patch('letsencrypt.cli.client') as client: + with mock.patch('letsencrypt.main.client') as client: ret, stdout, stderr = self._call_no_clientmock(args) return ret, stdout, stderr, client def _call_no_clientmock(self, args): "Run the client with output streams mocked out" args = self.standard_args + args - with mock.patch('letsencrypt.cli.sys.stdout') as stdout: - with mock.patch('letsencrypt.cli.sys.stderr') as stderr: + with mock.patch('letsencrypt.main.sys.stdout') as stdout: + with mock.patch('letsencrypt.main.sys.stderr') as stderr: ret = main.main(args[:]) # NOTE: parser can alter its args! return ret, stdout, stderr @@ -70,8 +70,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods caller. """ args = self.standard_args + args - with mock.patch('letsencrypt.cli.sys.stderr') as stderr: - with mock.patch('letsencrypt.cli.client') as client: + with mock.patch('letsencrypt.main.sys.stderr') as stderr: + with mock.patch('letsencrypt.main.client') as client: ret = main.main(args[:]) # NOTE: parser can alter its args! return ret, None, stderr, client @@ -83,7 +83,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _help_output(self, args): "Run a command, and return the ouput string for scrutiny" output = StringIO.StringIO() - with mock.patch('letsencrypt.cli.sys.stdout', new=output): + with mock.patch('letsencrypt.main.sys.stdout', new=output): self.assertRaises(SystemExit, self._call_stdout, args) out = output.getvalue() return out @@ -136,7 +136,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "Ensure that a particular error raises a missing cli flag error containing message" exc = None try: - with mock.patch('letsencrypt.cli.sys.stderr'): + with mock.patch('letsencrypt.main.sys.stderr'): main.main(self.standard_args + args[:]) # NOTE: parser can alter its args! except errors.MissingCommandlineFlag as exc: self.assertTrue(message in str(exc)) @@ -147,15 +147,15 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._cli_missing_flag(args, "specify a plugin") args.extend(['--standalone', '-d', 'eg.is']) self._cli_missing_flag(args, "register before running") - with mock.patch('letsencrypt.cli._auth_from_domains'): - with mock.patch('letsencrypt.cli.client.acme_from_config_key'): + with mock.patch('letsencrypt.main._auth_from_domains'): + with mock.patch('letsencrypt.main.client.acme_from_config_key'): args.extend(['--email', 'io@io.is']) self._cli_missing_flag(args, "--agree-tos") - @mock.patch('letsencrypt.cli.client.acme_client.Client') - @mock.patch('letsencrypt.cli._determine_account') - @mock.patch('letsencrypt.cli.client.Client.obtain_and_enroll_certificate') - @mock.patch('letsencrypt.cli._auth_from_domains') + @mock.patch('letsencrypt.main.client.acme_client.Client') + @mock.patch('letsencrypt.main._determine_account') + @mock.patch('letsencrypt.main.client.Client.obtain_and_enroll_certificate') + @mock.patch('letsencrypt.main._auth_from_domains') def test_user_agent(self, afd, _obt, det, _client): # Normally the client is totally mocked out, but here we need more # arguments to automate it... @@ -164,7 +164,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods det.return_value = mock.MagicMock(), None afd.return_value = mock.MagicMock(), "newcert" - with mock.patch('letsencrypt.cli.client.acme_client.ClientNetwork') as acme_net: + with mock.patch('letsencrypt.main.client.acme_client.ClientNetwork') as acme_net: self._call_no_clientmock(args) os_ver = " ".join(le_util.get_os_info()) ua = acme_net.call_args[1]["user_agent"] @@ -174,7 +174,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if "linux" in plat.lower(): self.assertTrue(platform.linux_distribution()[0] in ua) - with mock.patch('letsencrypt.cli.client.acme_client.ClientNetwork') as acme_net: + with mock.patch('letsencrypt.main.client.acme_client.ClientNetwork') as acme_net: ua = "bandersnatch" args += ["--user-agent", ua] self._call_no_clientmock(args) @@ -197,8 +197,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(args.chain_path, os.path.abspath(chain)) self.assertEqual(args.fullchain_path, os.path.abspath(fullchain)) - @mock.patch('letsencrypt.cli.record_chosen_plugins') - @mock.patch('letsencrypt.cli.display_ops') + @mock.patch('letsencrypt.main.cli.record_chosen_plugins') + @mock.patch('letsencrypt.main.cli.display_ops') def test_installer_selection(self, mock_display_ops, _rec): self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert', '--key-path', 'key', '--chain-path', 'chain']) @@ -237,8 +237,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._cli_missing_flag(["--standalone"], "With the standalone plugin, you probably") - with mock.patch("letsencrypt.cli._init_le_client") as mock_init: - with mock.patch("letsencrypt.cli._auth_from_domains") as mock_afd: + with mock.patch("letsencrypt.main._init_le_client") as mock_init: + with mock.patch("letsencrypt.main._auth_from_domains") as mock_afd: mock_afd.return_value = (mock.MagicMock(), mock.MagicMock()) self._call(["certonly", "--manual", "-d", "foo.bar"]) unused_config, auth, unused_installer = mock_init.call_args[0] @@ -267,8 +267,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods for r in xrange(len(flags)))): self._call(['plugins'] + list(args)) - @mock.patch('letsencrypt.cli.plugins_disco') - @mock.patch('letsencrypt.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('letsencrypt.main.plugins_disco') + @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_no_args(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -279,8 +279,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods filtered = plugins.visible().ifaces() stdout.write.called_once_with(str(filtered)) - @mock.patch('letsencrypt.cli.plugins_disco') - @mock.patch('letsencrypt.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('letsencrypt.main.plugins_disco') + @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_init(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -294,8 +294,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods verified = filtered.verify() stdout.write.called_once_with(str(verified)) - @mock.patch('letsencrypt.cli.plugins_disco') - @mock.patch('letsencrypt.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('letsencrypt.main.plugins_disco') + @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_prepare(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -504,9 +504,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods {"eg.com": "/tmp", "www.eg.com": "/tmp", "eg.is": "/tmp2"}) def _certonly_new_request_common(self, mock_client, args=None): - with mock.patch('letsencrypt.cli._treat_as_renewal') as mock_renewal: + with mock.patch('letsencrypt.main._treat_as_renewal') as mock_renewal: mock_renewal.return_value = ("newcert", None) - with mock.patch('letsencrypt.cli._init_le_client') as mock_init: + with mock.patch('letsencrypt.main._init_le_client') as mock_init: mock_init.return_value = mock_client if args is None: args = [] @@ -563,17 +563,17 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_client.obtain_certificate.return_value = (mock_certr, 'chain', mock_key, 'csr') try: - with mock.patch('letsencrypt.cli._find_duplicative_certs') as mock_fdc: + with mock.patch('letsencrypt.main._find_duplicative_certs') as mock_fdc: mock_fdc.return_value = (mock_lineage, None) - with mock.patch('letsencrypt.cli._init_le_client') as mock_init: + with mock.patch('letsencrypt.main._init_le_client') as mock_init: mock_init.return_value = mock_client - get_utility_path = 'letsencrypt.cli.zope.component.getUtility' + get_utility_path = 'letsencrypt.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: - with mock.patch('letsencrypt.cli.OpenSSL') as mock_ssl: + with mock.patch('letsencrypt.main.OpenSSL') as mock_ssl: mock_latest = mock.MagicMock() mock_latest.get_issuer.return_value = "Fake fake" mock_ssl.crypto.load_certificate.return_value = mock_latest - with mock.patch('letsencrypt.cli.crypto_util'): + with mock.patch('letsencrypt.main.crypto_util'): if not args: args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly'] if extra_args: @@ -689,7 +689,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if names is not None: mock_lineage.names.return_value = names mock_rc.return_value = mock_lineage - with mock.patch('letsencrypt.cli.obtain_cert') as mock_obtain_cert: + with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: self._test_renewal_common(True, None, error_expected=error_expected, args=['renew'], renew=False) if assert_oc_called is not None: @@ -738,7 +738,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_rc.return_value = mock_lineage mock_lineage.configuration = { 'renewalparams': {'authenticator': 'webroot'}} - with mock.patch('letsencrypt.cli.obtain_cert') as mock_obtain_cert: + with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: mock_obtain_cert.side_effect = Exception self._test_renewal_common(True, None, error_expected=True, args=['renew'], renew=False) @@ -750,8 +750,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods renew=False, error_expected=True) @mock.patch('letsencrypt.cli.zope.component.getUtility') - @mock.patch('letsencrypt.cli._treat_as_renewal') - @mock.patch('letsencrypt.cli._init_le_client') + @mock.patch('letsencrypt.main._treat_as_renewal') + @mock.patch('letsencrypt.main._init_le_client') def test_certonly_reinstall(self, mock_init, mock_renewal, mock_get_utility): mock_renewal.return_value = ('reinstall', mock.MagicMock()) mock_init.return_value = mock_client = mock.MagicMock() @@ -768,9 +768,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_client.obtain_certificate_from_csr.return_value = (certr, chain) cert_path = '/etc/letsencrypt/live/example.com/cert.pem' mock_client.save_certificate.return_value = cert_path, None, None - with mock.patch('letsencrypt.cli._init_le_client') as mock_init: + with mock.patch('letsencrypt.main._init_le_client') as mock_init: mock_init.return_value = mock_client - get_utility_path = 'letsencrypt.cli.zope.component.getUtility' + get_utility_path = 'letsencrypt.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: chain_path = '/etc/letsencrypt/live/example.com/chain.pem' full_path = '/etc/letsencrypt/live/example.com/fullchain.pem' @@ -779,7 +779,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods CSR, cert_path, chain_path, full_path).split() if extra_args: args += extra_args - with mock.patch('letsencrypt.cli.crypto_util'): + with mock.patch('letsencrypt.main.crypto_util'): self._call(args) if '--dry-run' in args: @@ -803,7 +803,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue( 'dry run' in mock_get_utility().add_message.call_args[0][0]) - @mock.patch('letsencrypt.cli.client.acme_client') + @mock.patch('letsencrypt.main.client.acme_client') def test_revoke_with_key(self, mock_acme_client): server = 'foo.bar' self._call_no_clientmock(['--cert-path', CERT, '--key-path', KEY, @@ -816,7 +816,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_revoke = mock_acme_client.Client().revoke mock_revoke.assert_called_once_with(jose.ComparableX509(cert)) - @mock.patch('letsencrypt.cli._determine_account') + @mock.patch('letsencrypt.main._determine_account') def test_revoke_without_key(self, mock_determine_account): mock_determine_account.return_value = (mock.MagicMock(), None) _, _, _, client = self._call(['--cert-path', CERT, 'revoke']) @@ -825,7 +825,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_revoke = client.acme_from_config_key().revoke mock_revoke.assert_called_once_with(jose.ComparableX509(cert)) - @mock.patch('letsencrypt.cli.sys') + @mock.patch('letsencrypt.main.sys') def test_handle_exception(self, mock_sys): # pylint: disable=protected-access from acme import messages @@ -833,7 +833,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods config = mock.MagicMock() mock_open = mock.mock_open() - with mock.patch('letsencrypt.cli.open', mock_open, create=True): + with mock.patch('letsencrypt.main.open', mock_open, create=True): exception = Exception('detail') config.verbose_count = 1 main._handle_exception( @@ -843,7 +843,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods error_msg = mock_sys.exit.call_args_list[0][0][0] self.assertTrue('unexpected error' in error_msg) - with mock.patch('letsencrypt.cli.open', mock_open, create=True): + with mock.patch('letsencrypt.main.open', mock_open, create=True): mock_open.side_effect = [KeyboardInterrupt] error = errors.Error('detail') main._handle_exception( @@ -908,7 +908,7 @@ class DetermineAccountTest(unittest.TestCase): def _call(self): # pylint: disable=protected-access from letsencrypt.main import _determine_account - with mock.patch('letsencrypt.cli.account.AccountFileStorage') as mock_storage: + with mock.patch('letsencrypt.main.account.AccountFileStorage') as mock_storage: mock_storage.return_value = self.account_storage return _determine_account(self.config) @@ -940,7 +940,7 @@ class DetermineAccountTest(unittest.TestCase): def test_no_accounts_no_email(self, mock_get_email): mock_get_email.return_value = 'foo@bar.baz' - with mock.patch('letsencrypt.cli.client') as client: + with mock.patch('letsencrypt.main.client') as client: client.register.return_value = ( self.accs[0], mock.sentinel.acme) self.assertEqual((self.accs[0], mock.sentinel.acme), self._call()) @@ -952,7 +952,7 @@ class DetermineAccountTest(unittest.TestCase): def test_no_accounts_email(self): self.config.email = 'other email' - with mock.patch('letsencrypt.cli.client') as client: + with mock.patch('letsencrypt.main.client') as client: client.register.return_value = (self.accs[1], mock.sentinel.acme) self._call() self.assertEqual(self.accs[1].id, self.config.account) @@ -1014,7 +1014,7 @@ class MockedVerb(object): """ def __init__(self, verb_name): - self.verb_dict = cli.HelpfulArgumentParser.VERBS + self.verb_dict = main.VERBS self.verb_func = None self.verb_name = verb_name From 683bebd56c841bc53bc7d13480ff6440848bf05e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 10 Mar 2016 18:31:29 -0800 Subject: [PATCH 016/317] Lint --- letsencrypt/cli.py | 1 + letsencrypt/main.py | 2 +- letsencrypt/tests/display/util_test.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 5ed97d03f..508eb48ad 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -29,6 +29,7 @@ from letsencrypt import storage from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco +# pylint: disable=too-many-lines logger = logging.getLogger(__name__) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index e8b3f7345..56725d300 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -701,7 +701,7 @@ def main(cli_args=sys.argv[1:]): # Maps verbs/subcommands to the functions that implement them -# In principle this should live in cli.HelpfulArgumentParser, but +# In principle this should live in cli.HelpfulArgumentParser, but # due to issues with import cycles and testing, it lives here VERBS = {"auth": obtain_cert, "certonly": obtain_cert, "config_changes": config_changes, "everything": run, diff --git a/letsencrypt/tests/display/util_test.py b/letsencrypt/tests/display/util_test.py index a16eb544e..3f8ee8bb5 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/letsencrypt/tests/display/util_test.py @@ -8,7 +8,6 @@ import letsencrypt.errors as errors from letsencrypt.display import util as display_util - CHOICES = [("First", "Description1"), ("Second", "Description2")] TAGS = ["tag1", "tag2", "tag3"] TAGS_CHOICES = [("1", "tag1"), ("2", "tag2"), ("3", "tag3")] From 3c3c6ce359db78d586a417b3f79d50475d8b41c0 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 10 Mar 2016 18:54:03 -0800 Subject: [PATCH 017/317] Fight with cyclic lint --- .pylintrc | 2 +- letsencrypt/cli.py | 4 ++-- letsencrypt/renew.py | 0 3 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 letsencrypt/renew.py diff --git a/.pylintrc b/.pylintrc index 92dde98c0..49d0f29ea 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,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes +disable=fixme,locally-disabled,abstract-class-not-used,abstract-class-little-used,bad-continuation,too-few-public-methods,no-self-use,invalid-name,too-many-instance-attributes,cyclic-import # abstract-class-not-used cannot be disabled locally (at least in # pylint 1.4.1), same for abstract-class-little-used diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index dee43eac1..b76311777 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1,4 +1,5 @@ """Let's Encrypt command CLI argument processing.""" +# pylint: disable=too-many-lines from __future__ import print_function import argparse import copy @@ -30,7 +31,6 @@ from letsencrypt import storage from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco -# pylint: disable=too-many-lines logger = logging.getLogger(__name__) @@ -635,7 +635,7 @@ class HelpfulArgumentParser(object): self.VERBS = main.VERBS # List of topics for which additional help can be provided HELP_TOPICS = ["all", "security", - "paths", "automation", "testing"] + list(six.iterkeys(self.VERBS) + "paths", "automation", "testing"] + list(six.iterkeys(self.VERBS)) plugin_names = list(six.iterkeys(plugins)) self.help_topics = HELP_TOPICS + plugin_names + [None] diff --git a/letsencrypt/renew.py b/letsencrypt/renew.py new file mode 100644 index 000000000..e69de29bb From d468e9926eeae9b3fbc534461dd6dce2a04a41a8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 10 Mar 2016 18:58:49 -0800 Subject: [PATCH 018/317] Fix stray mockery --- letsencrypt/tests/client_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 7dd513e18..8be2178b9 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -128,7 +128,7 @@ class ClientTest(unittest.TestCase): # FIXME move parts of this to test_cli.py... @mock.patch("letsencrypt.client.logger") - @mock.patch("letsencrypt.cli._process_domain") + @mock.patch("letsencrypt.cli.process_domain") def test_obtain_certificate_from_csr(self, mock_process_domain, mock_logger): self._mock_obtain_certificate() from letsencrypt import cli From 2656d97260c3bd5eb074a70de5b6a06ce57a37d5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 11:53:58 -0800 Subject: [PATCH 019/317] Update entry point --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b187e6fdb..87cef2cb2 100644 --- a/setup.py +++ b/setup.py @@ -127,7 +127,7 @@ setup( entry_points={ 'console_scripts': [ - 'letsencrypt = letsencrypt.cli:main', + 'letsencrypt = letsencrypt.main:main', ], 'letsencrypt.plugins': [ 'manual = letsencrypt.plugins.manual:Authenticator', From 388baa5a1eb49f22187548a6a7dee5d38dfe98aa Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 12:29:31 -0800 Subject: [PATCH 020/317] Start splitting renew.py out of cli.py --- letsencrypt/cli.py | 306 +--------------------------------- letsencrypt/main.py | 14 +- letsencrypt/plugins/common.py | 2 +- letsencrypt/renew.py | 304 +++++++++++++++++++++++++++++++++ letsencrypt/tests/cli_test.py | 24 +-- 5 files changed, 333 insertions(+), 317 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b76311777..e34bd971b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -2,7 +2,6 @@ # pylint: disable=too-many-lines from __future__ import print_function import argparse -import copy import glob import json import logging @@ -14,19 +13,14 @@ import traceback import configargparse import OpenSSL import six -import zope.component -import zope.interface.exceptions -import zope.interface.verify import letsencrypt -from letsencrypt import configuration from letsencrypt import constants from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util -from letsencrypt import storage from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco @@ -35,16 +29,7 @@ from letsencrypt.plugins import disco as plugins_disco logger = logging.getLogger(__name__) # Global, to save us from a lot of argument passing within the scope of this module -_parser = None - -# These are the items which get pulled out of a renewal configuration -# file's renewalparams and actually used in the client configuration -# during the renewal process. We have to record their types here because -# the renewal configuration process loses this information. -STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent", - "server", "account", "authenticator", "installer", - "standalone_supported_challenges"] -INT_CONFIG_ITEMS = ["rsa_key_size", "tls_sni_01_port", "http01_port"] +helpful_parser = None # For help strings, figure out how the user ran us. # When invoked from letsencrypt-auto, sys.argv[0] is something like: @@ -115,21 +100,6 @@ def usage_strings(plugins): return USAGE % (apache_doc, nginx_doc), SHORT_USAGE -def should_renew(config, lineage): - "Return true if any of the circumstances for automatic renewal apply." - if config.renew_by_default: - logger.info("Auto-renewal forced with --force-renewal...") - return True - if lineage.should_autorenew(interactive=True): - logger.info("Cert is due for renewal, auto-renewing...") - return True - if config.dry_run: - logger.info("Cert not due for renewal, but simulating renewal for dry run") - return True - logger.info("Cert not yet due for renewal") - return False - - def diagnose_configurator_problem(cfg_type, requested, plugins): """ Raise the most helpful error message about a plugin being unavailable @@ -271,20 +241,20 @@ def record_chosen_plugins(config, plugins, auth, inst): cn.installer = plugins.find_init(inst).name if inst else "none" -def _set_by_cli(var): +def set_by_cli(var): """ Return True if a particular config variable has been set by the user (CLI or config file) including if the user explicitly set it to the default. Returns False if the variable was assigned a default value. """ - detector = _set_by_cli.detector + detector = set_by_cli.detector if detector is None: # Setup on first run: `detector` is a weird version of config in which # the default value of every attribute is wrangled to be boolean-false plugins = plugins_disco.PluginsRegistry.find_all() # reconstructed_args == sys.argv[1:], or whatever was passed to main() - reconstructed_args = _parser.args + [_parser.verb] - detector = _set_by_cli.detector = prepare_and_parse_args( + reconstructed_args = helpful_parser.args + [helpful_parser.verb] + detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator auth, inst = cli_plugin_requests(detector) @@ -312,265 +282,7 @@ def _set_by_cli(var): else: return False # static housekeeping var -_set_by_cli.detector = None - -def _restore_required_config_elements(config, renewalparams): - """Sets non-plugin specific values in config from renewalparams - - :param configuration.NamespaceConfig config: configuration for the - current lineage - :param configobj.Section renewalparams: parameters from the renewal - configuration file that defines this lineage - - """ - # string-valued items to add if they're present - for config_item in STR_CONFIG_ITEMS: - if config_item in renewalparams and not _set_by_cli(config_item): - value = renewalparams[config_item] - # Unfortunately, we've lost type information from ConfigObj, - # so we don't know if the original was NoneType or str! - if value == "None": - value = None - setattr(config.namespace, config_item, value) - # int-valued items to add if they're present - for config_item in INT_CONFIG_ITEMS: - if config_item in renewalparams and not _set_by_cli(config_item): - config_value = renewalparams[config_item] - # the default value for http01_port was None during private beta - if config_item == "http01_port" and config_value == "None": - logger.info("updating legacy http01_port value") - int_value = flag_default("http01_port") - else: - try: - int_value = int(config_value) - except ValueError: - raise errors.Error( - "Expected a numeric value for {0}".format(config_item)) - setattr(config.namespace, config_item, int_value) - - -def _restore_plugin_configs(config, renewalparams): - """Sets plugin specific values in config from renewalparams - - :param configuration.NamespaceConfig config: configuration for the - current lineage - :param configobj.Section renewalparams: Parameters from the renewal - configuration file that defines this lineage - - """ - # Now use parser to get plugin-prefixed items with correct types - # XXX: the current approach of extracting only prefixed items - # related to the actually-used installer and authenticator - # works as long as plugins don't need to read plugin-specific - # variables set by someone else (e.g., assuming Apache - # configurator doesn't need to read webroot_ variables). - # Note: if a parameter that used to be defined in the parser is no - # longer defined, stored copies of that parameter will be - # deserialized as strings by this logic even if they were - # originally meant to be some other type. - if renewalparams["authenticator"] == "webroot": - _restore_webroot_config(config, renewalparams) - plugin_prefixes = [] - else: - plugin_prefixes = [renewalparams["authenticator"]] - - if renewalparams.get("installer", None) is not None: - plugin_prefixes.append(renewalparams["installer"]) - for plugin_prefix in set(plugin_prefixes): - for config_item, config_value in six.iteritems(renewalparams): - if config_item.startswith(plugin_prefix + "_") and not _set_by_cli(config_item): - # Values None, True, and False need to be treated specially, - # As they don't get parsed correctly based on type - if config_value in ("None", "True", "False"): - # bool("False") == True - # pylint: disable=eval-used - setattr(config.namespace, config_item, eval(config_value)) - continue - for action in _parser.parser._actions: # pylint: disable=protected-access - if action.type is not None and action.dest == config_item: - setattr(config.namespace, config_item, - action.type(config_value)) - break - else: - setattr(config.namespace, config_item, str(config_value)) - -def _restore_webroot_config(config, renewalparams): - """ - webroot_map is, uniquely, a dict, and the general-purpose configuration - restoring logic is not able to correctly parse it from the serialized - form. - """ - if "webroot_map" in renewalparams: - # if the user does anything that would create a new webroot map on the - # CLI, don't use the old one - if not (_set_by_cli("webroot_map") or _set_by_cli("webroot_path")): - setattr(config.namespace, "webroot_map", renewalparams["webroot_map"]) - elif "webroot_path" in renewalparams: - logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path") - wp = renewalparams["webroot_path"] - if isinstance(wp, str): # prior to 0.1.0, webroot_path was a string - wp = [wp] - setattr(config.namespace, "webroot_path", wp) - - -def _reconstitute(config, full_path): - """Try to instantiate a RenewableCert, updating config with relevant items. - - This is specifically for use in renewal and enforces several checks - and policies to ensure that we can try to proceed with the renwal - request. The config argument is modified by including relevant options - read from the renewal configuration file. - - :param configuration.NamespaceConfig config: configuration for the - current lineage - :param str full_path: Absolute path to the configuration file that - defines this lineage - - :returns: the RenewableCert object or None if a fatal error occurred - :rtype: `storage.RenewableCert` or NoneType - - """ - try: - renewal_candidate = storage.RenewableCert( - full_path, configuration.RenewerConfiguration(config)) - except (errors.CertStorageError, IOError): - logger.warning("Renewal configuration file %s is broken. Skipping.", full_path) - logger.debug("Traceback was:\n%s", traceback.format_exc()) - return None - if "renewalparams" not in renewal_candidate.configuration: - logger.warning("Renewal configuration file %s lacks " - "renewalparams. Skipping.", full_path) - return None - renewalparams = renewal_candidate.configuration["renewalparams"] - if "authenticator" not in renewalparams: - logger.warning("Renewal configuration file %s does not specify " - "an authenticator. Skipping.", full_path) - return None - # Now restore specific values along with their data types, if - # those elements are present. - try: - _restore_required_config_elements(config, renewalparams) - _restore_plugin_configs(config, renewalparams) - except (ValueError, errors.Error) as error: - logger.warning( - "An error occured while parsing %s. The error was %s. " - "Skipping the file.", full_path, error.message) - logger.debug("Traceback was:\n%s", traceback.format_exc()) - return None - - try: - for d in renewal_candidate.names(): - process_domain(config, d) - except errors.ConfigurationError as error: - logger.warning("Renewal configuration file %s references a cert " - "that contains an invalid domain name. The problem " - "was: %s. Skipping.", full_path, error) - return None - - return renewal_candidate - -def _renewal_conf_files(config): - """Return /path/to/*.conf in the renewal conf directory""" - return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf")) - - -def _renew_describe_results(config, renew_successes, renew_failures, - renew_skipped, parse_failures): - status = lambda x, msg: " " + "\n ".join(i + " (" + msg +")" for i in x) - if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates below have not been saved.)") - print() - if renew_skipped: - print("The following certs are not due for renewal yet:") - print(status(renew_skipped, "skipped")) - if not renew_successes and not renew_failures: - print("No renewals were attempted.") - elif renew_successes and not renew_failures: - print("Congratulations, all renewals succeeded. The following certs " - "have been renewed:") - print(status(renew_successes, "success")) - elif renew_failures and not renew_successes: - print("All renewal attempts failed. The following certs could not be " - "renewed:") - print(status(renew_failures, "failure")) - elif renew_failures and renew_successes: - print("The following certs were successfully renewed:") - print(status(renew_successes, "success")) - print("\nThe following certs could not be renewed:") - print(status(renew_failures, "failure")) - - if parse_failures: - print("\nAdditionally, the following renewal configuration files " - "were invalid: ") - print(status(parse_failures, "parsefail")) - - if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates above have not been saved.)") - - -def renew(config, unused_plugins): - """Renew previously-obtained certificates.""" - - if config.domains != []: - raise errors.Error("Currently, the renew verb is only capable of " - "renewing all installed certificates that are due " - "to be renewed; individual domains cannot be " - "specified with this action. If you would like to " - "renew specific certificates, use the certonly " - "command. The renew verb may provide other options " - "for selecting certificates to renew in the future.") - renewer_config = configuration.RenewerConfiguration(config) - renew_successes = [] - renew_failures = [] - renew_skipped = [] - parse_failures = [] - for renewal_file in _renewal_conf_files(renewer_config): - print("Processing " + renewal_file) - lineage_config = copy.deepcopy(config) - - # Note that this modifies config (to add back the configuration - # elements from within the renewal configuration file). - try: - renewal_candidate = _reconstitute(lineage_config, renewal_file) - except Exception as e: # pylint: disable=broad-except - logger.warning("Renewal configuration file %s produced an " - "unexpected error: %s. Skipping.", renewal_file, e) - logger.debug("Traceback was:\n%s", traceback.format_exc()) - parse_failures.append(renewal_file) - continue - - try: - if renewal_candidate is None: - parse_failures.append(renewal_file) - else: - # XXX: ensure that each call here replaces the previous one - zope.component.provideUtility(lineage_config) - if should_renew(lineage_config, renewal_candidate): - plugins = plugins_disco.PluginsRegistry.find_all() - from letsencrypt import main - main.obtain_cert(lineage_config, plugins, renewal_candidate) - renew_successes.append(renewal_candidate.fullchain) - else: - renew_skipped.append(renewal_candidate.fullchain) - except Exception as e: # pylint: disable=broad-except - # obtain_cert (presumably) encountered an unanticipated problem. - logger.warning("Attempting to renew cert from %s produced an " - "unexpected error: %s. Skipping.", renewal_file, e) - logger.debug("Traceback was:\n%s", traceback.format_exc()) - renew_failures.append(renewal_candidate.fullchain) - - # Describe all the results - _renew_describe_results(config, renew_successes, renew_failures, - renew_skipped, parse_failures) - - if renew_failures or parse_failures: - raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format( - len(renew_failures), len(parse_failures))) - else: - logger.debug("no renewal failures") - +set_by_cli.detector = None def read_file(filename, mode="rb"): """Returns the given file's contents. @@ -839,7 +551,7 @@ class HelpfulArgumentParser(object): def modify_arg_for_default_detection(self, *args, **kwargs): """ Adding an arg, but ensure that it has a default that evaluates to false, - so that _set_by_cli can tell if it was set. Only called if detect_defaults==True. + so that set_by_cli can tell if it was set. Only called if detect_defaults==True. :param list *args: the names of this argument flag :param dict **kwargs: various argparse settings for this argument @@ -1108,8 +820,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): _plugins_parsing(helpful, plugins) if not detect_defaults: - global _parser # pylint: disable=global-statement - _parser = helpful + global helpful_parser # pylint: disable=global-statement + helpful_parser = helpful return helpful.parse_args() diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 56725d300..8d59993df 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -20,9 +20,9 @@ from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt import log from letsencrypt import reporter +from letsencrypt import renew from letsencrypt import storage -from letsencrypt.cli import choose_configurator_plugins, _renewal_conf_files, should_renew from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco @@ -184,7 +184,7 @@ def _handle_identical_cert_request(config, cert): :rtype: tuple """ - if should_renew(config, cert): + if renew.should_renew(config, cert): return "renew", cert if config.reinstall: # Set with --reinstall, force an identical certificate to be @@ -261,7 +261,7 @@ def _find_duplicative_certs(config, domains): # Verify the directory is there le_util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid()) - for renewal_file in _renewal_conf_files(cli_config): + for renewal_file in renew.renewal_conf_files(cli_config): try: candidate_lineage = storage.RenewableCert(renewal_file, cli_config) except (errors.CertStorageError, IOError): @@ -404,7 +404,7 @@ def install(config, plugins): # this function ... try: - installer, _ = choose_configurator_plugins(config, plugins, "install") + installer, _ = cli.choose_configurator_plugins(config, plugins, "install") except errors.PluginSelectionError as e: return e.message @@ -479,7 +479,7 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals # TODO: Make run as close to auth + install as possible # Possible difficulties: config.csr was hacked into auth try: - installer, authenticator = choose_configurator_plugins(config, plugins, "run") + installer, authenticator = cli.choose_configurator_plugins(config, plugins, "run") except errors.PluginSelectionError as e: return e.message @@ -509,7 +509,7 @@ def obtain_cert(config, plugins, lineage=None): # pylint: disable=too-many-locals try: # installers are used in auth mode to determine domain names - installer, authenticator = choose_configurator_plugins(config, plugins, "certonly") + installer, authenticator = cli.choose_configurator_plugins(config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise @@ -705,7 +705,7 @@ def main(cli_args=sys.argv[1:]): # due to issues with import cycles and testing, it lives here VERBS = {"auth": obtain_cert, "certonly": obtain_cert, "config_changes": config_changes, "everything": run, - "install": install, "plugins": plugins_cmd, "renew": cli.renew, + "install": install, "plugins": plugins_cmd, "renew": renew.renew, "revoke": revoke, "rollback": rollback, "run": run} diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 319692344..f6a2c3d76 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -52,7 +52,7 @@ class Plugin(object): NOTE: if you add argpase arguments such that users setting them can create a config entry that python's bool() would consider false (ie, the use might set the variable to "", [], 0, etc), please ensure that - cli._set_by_cli() works for your variable. + cli.set_by_cli() works for your variable. """ diff --git a/letsencrypt/renew.py b/letsencrypt/renew.py index e69de29bb..6eb3fa27c 100644 --- a/letsencrypt/renew.py +++ b/letsencrypt/renew.py @@ -0,0 +1,304 @@ +"""Functionality for autorenewal and associated juggling of configurations""" +from __future__ import print_function +import copy +import glob +import logging +import os +import traceback + +import six +import zope.component + +from letsencrypt import configuration +from letsencrypt import cli +from letsencrypt import errors +from letsencrypt import storage +from letsencrypt.plugins import disco as plugins_disco + +logger = logging.getLogger(__name__) + +# These are the items which get pulled out of a renewal configuration +# file's renewalparams and actually used in the client configuration +# during the renewal process. We have to record their types here because +# the renewal configuration process loses this information. +STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent", + "server", "account", "authenticator", "installer", + "standalone_supported_challenges"] +INT_CONFIG_ITEMS = ["rsa_key_size", "tls_sni_01_port", "http01_port"] + + +def _renew_describe_results(config, renew_successes, renew_failures, + renew_skipped, parse_failures): + status = lambda x, msg: " " + "\n ".join(i + " (" + msg +")" for i in x) + if config.dry_run: + print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + print("** (The test certificates below have not been saved.)") + print() + if renew_skipped: + print("The following certs are not due for renewal yet:") + print(status(renew_skipped, "skipped")) + if not renew_successes and not renew_failures: + print("No renewals were attempted.") + elif renew_successes and not renew_failures: + print("Congratulations, all renewals succeeded. The following certs " + "have been renewed:") + print(status(renew_successes, "success")) + elif renew_failures and not renew_successes: + print("All renewal attempts failed. The following certs could not be " + "renewed:") + print(status(renew_failures, "failure")) + elif renew_failures and renew_successes: + print("The following certs were successfully renewed:") + print(status(renew_successes, "success")) + print("\nThe following certs could not be renewed:") + print(status(renew_failures, "failure")) + + if parse_failures: + print("\nAdditionally, the following renewal configuration files " + "were invalid: ") + print(status(parse_failures, "parsefail")) + + if config.dry_run: + print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + print("** (The test certificates above have not been saved.)") + + +def renewal_conf_files(config): + """Return /path/to/*.conf in the renewal conf directory""" + return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf")) + + +def _reconstitute(config, full_path): + """Try to instantiate a RenewableCert, updating config with relevant items. + + This is specifically for use in renewal and enforces several checks + and policies to ensure that we can try to proceed with the renwal + request. The config argument is modified by including relevant options + read from the renewal configuration file. + + :param configuration.NamespaceConfig config: configuration for the + current lineage + :param str full_path: Absolute path to the configuration file that + defines this lineage + + :returns: the RenewableCert object or None if a fatal error occurred + :rtype: `storage.RenewableCert` or NoneType + + """ + try: + renewal_candidate = storage.RenewableCert( + full_path, configuration.RenewerConfiguration(config)) + except (errors.CertStorageError, IOError): + logger.warning("Renewal configuration file %s is broken. Skipping.", full_path) + logger.debug("Traceback was:\n%s", traceback.format_exc()) + return None + if "renewalparams" not in renewal_candidate.configuration: + logger.warning("Renewal configuration file %s lacks " + "renewalparams. Skipping.", full_path) + return None + renewalparams = renewal_candidate.configuration["renewalparams"] + if "authenticator" not in renewalparams: + logger.warning("Renewal configuration file %s does not specify " + "an authenticator. Skipping.", full_path) + return None + # Now restore specific values along with their data types, if + # those elements are present. + try: + _restore_required_config_elements(config, renewalparams) + _restore_plugin_configs(config, renewalparams) + except (ValueError, errors.Error) as error: + logger.warning( + "An error occured while parsing %s. The error was %s. " + "Skipping the file.", full_path, error.message) + logger.debug("Traceback was:\n%s", traceback.format_exc()) + return None + + try: + for d in renewal_candidate.names(): + cli.process_domain(config, d) + except errors.ConfigurationError as error: + logger.warning("Renewal configuration file %s references a cert " + "that contains an invalid domain name. The problem " + "was: %s. Skipping.", full_path, error) + return None + + return renewal_candidate + + +def _restore_webroot_config(config, renewalparams): + """ + webroot_map is, uniquely, a dict, and the general-purpose configuration + restoring logic is not able to correctly parse it from the serialized + form. + """ + if "webroot_map" in renewalparams: + # if the user does anything that would create a new webroot map on the + # CLI, don't use the old one + if not (cli.set_by_cli("webroot_map") or cli.set_by_cli("webroot_path")): + setattr(config.namespace, "webroot_map", renewalparams["webroot_map"]) + elif "webroot_path" in renewalparams: + logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path") + wp = renewalparams["webroot_path"] + if isinstance(wp, str): # prior to 0.1.0, webroot_path was a string + wp = [wp] + setattr(config.namespace, "webroot_path", wp) + + +def _restore_plugin_configs(config, renewalparams): + """Sets plugin specific values in config from renewalparams + + :param configuration.NamespaceConfig config: configuration for the + current lineage + :param configobj.Section renewalparams: Parameters from the renewal + configuration file that defines this lineage + + """ + # Now use parser to get plugin-prefixed items with correct types + # XXX: the current approach of extracting only prefixed items + # related to the actually-used installer and authenticator + # works as long as plugins don't need to read plugin-specific + # variables set by someone else (e.g., assuming Apache + # configurator doesn't need to read webroot_ variables). + # Note: if a parameter that used to be defined in the parser is no + # longer defined, stored copies of that parameter will be + # deserialized as strings by this logic even if they were + # originally meant to be some other type. + if renewalparams["authenticator"] == "webroot": + _restore_webroot_config(config, renewalparams) + plugin_prefixes = [] + else: + plugin_prefixes = [renewalparams["authenticator"]] + + if renewalparams.get("installer", None) is not None: + plugin_prefixes.append(renewalparams["installer"]) + for plugin_prefix in set(plugin_prefixes): + for config_item, config_value in six.iteritems(renewalparams): + if config_item.startswith(plugin_prefix + "_") and not cli.set_by_cli(config_item): + # Values None, True, and False need to be treated specially, + # As they don't get parsed correctly based on type + if config_value in ("None", "True", "False"): + # bool("False") == True + # pylint: disable=eval-used + setattr(config.namespace, config_item, eval(config_value)) + continue + # If argparse has a type for this variable, use it: + # pylint: disable=protected-access + for action in cli.helpful_parser.parser._actions: + if action.type is not None and action.dest == config_item: + setattr(config.namespace, config_item, + action.type(config_value)) + break + else: + setattr(config.namespace, config_item, str(config_value)) + + +def _restore_required_config_elements(config, renewalparams): + """Sets non-plugin specific values in config from renewalparams + + :param configuration.NamespaceConfig config: configuration for the + current lineage + :param configobj.Section renewalparams: parameters from the renewal + configuration file that defines this lineage + + """ + # string-valued items to add if they're present + for config_item in STR_CONFIG_ITEMS: + if config_item in renewalparams and not cli.set_by_cli(config_item): + value = renewalparams[config_item] + # Unfortunately, we've lost type information from ConfigObj, + # so we don't know if the original was NoneType or str! + if value == "None": + value = None + setattr(config.namespace, config_item, value) + # int-valued items to add if they're present + for config_item in INT_CONFIG_ITEMS: + if config_item in renewalparams and not cli.set_by_cli(config_item): + config_value = renewalparams[config_item] + # the default value for http01_port was None during private beta + if config_item == "http01_port" and config_value == "None": + logger.info("updating legacy http01_port value") + int_value = cli.flag_default("http01_port") + else: + try: + int_value = int(config_value) + except ValueError: + raise errors.Error( + "Expected a numeric value for {0}".format(config_item)) + setattr(config.namespace, config_item, int_value) + + +def should_renew(config, lineage): + "Return true if any of the circumstances for automatic renewal apply." + if config.renew_by_default: + logger.info("Auto-renewal forced with --force-renewal...") + return True + if lineage.should_autorenew(interactive=True): + logger.info("Cert is due for renewal, auto-renewing...") + return True + if config.dry_run: + logger.info("Cert not due for renewal, but simulating renewal for dry run") + return True + logger.info("Cert not yet due for renewal") + return False + + +def renew(config, unused_plugins): + """Renew previously-obtained certificates.""" + + if config.domains != []: + raise errors.Error("Currently, the renew verb is only capable of " + "renewing all installed certificates that are due " + "to be renewed; individual domains cannot be " + "specified with this action. If you would like to " + "renew specific certificates, use the certonly " + "command. The renew verb may provide other options " + "for selecting certificates to renew in the future.") + renewer_config = configuration.RenewerConfiguration(config) + renew_successes = [] + renew_failures = [] + renew_skipped = [] + parse_failures = [] + for renewal_file in renewal_conf_files(renewer_config): + print("Processing " + renewal_file) + lineage_config = copy.deepcopy(config) + + # Note that this modifies config (to add back the configuration + # elements from within the renewal configuration file). + try: + renewal_candidate = _reconstitute(lineage_config, renewal_file) + except Exception as e: # pylint: disable=broad-except + logger.warning("Renewal configuration file %s produced an " + "unexpected error: %s. Skipping.", renewal_file, e) + logger.debug("Traceback was:\n%s", traceback.format_exc()) + parse_failures.append(renewal_file) + continue + + try: + if renewal_candidate is None: + parse_failures.append(renewal_file) + else: + # XXX: ensure that each call here replaces the previous one + zope.component.provideUtility(lineage_config) + if should_renew(lineage_config, renewal_candidate): + plugins = plugins_disco.PluginsRegistry.find_all() + from letsencrypt import main + main.obtain_cert(lineage_config, plugins, renewal_candidate) + renew_successes.append(renewal_candidate.fullchain) + else: + renew_skipped.append(renewal_candidate.fullchain) + except Exception as e: # pylint: disable=broad-except + # obtain_cert (presumably) encountered an unanticipated problem. + logger.warning("Attempting to renew cert from %s produced an " + "unexpected error: %s. Skipping.", renewal_file, e) + logger.debug("Traceback was:\n%s", traceback.format_exc()) + renew_failures.append(renewal_candidate.fullchain) + + # Describe all the results + _renew_describe_results(config, renew_successes, renew_failures, + renew_skipped, parse_failures) + + if renew_failures or parse_failures: + raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format( + len(renew_failures), len(parse_failures))) + else: + logger.debug("no renewal failures") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index c5865206d..e79bcb95a 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -1,5 +1,4 @@ """Tests for letsencrypt.cli.""" - from __future__ import print_function import argparse @@ -24,6 +23,7 @@ from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import le_util from letsencrypt import main +from letsencrypt import renew from letsencrypt import storage from letsencrypt.plugins import disco @@ -555,7 +555,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._certonly_new_request_common, mock_client) def _test_renewal_common(self, due_for_renewal, extra_args, log_out=None, - args=None, renew=True, error_expected=False): + args=None, should_renew=True, error_expected=False): # pylint: disable=too-many-locals,too-many-arguments cert_path = 'letsencrypt/tests/testdata/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' @@ -594,7 +594,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "Unexpected renewal error:\n" + traceback.format_exc()) - if renew: + if should_renew: mock_client.obtain_certificate.assert_called_once_with(['isnot.org']) else: self.assertEqual(mock_client.obtain_certificate.call_count, 0) @@ -629,7 +629,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(get_utility().add_message.call_count, 1) _, _ = self._test_renewal_common(False, ['-tvv', '--debug', '--keep'], - log_out="not yet due", renew=False) + log_out="not yet due", should_renew=False) def _dump_log(self): with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf: @@ -652,9 +652,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_renew_verb(self): self._make_test_renewal_conf('sample-renewal.conf') args = ["renew", "--dry-run", "-tvv"] - self._test_renewal_common(True, [], args=args, renew=True) + self._test_renewal_common(True, [], args=args, should_renew=True) - @mock.patch("letsencrypt.cli._set_by_cli") + @mock.patch("letsencrypt.cli.set_by_cli") def test_ancient_webroot_renewal_conf(self, mock_set_by_cli): mock_set_by_cli.return_value = False rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf') @@ -664,7 +664,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods configuration.RenewerConfiguration(config)) renewalparams = lineage.configuration["renewalparams"] # pylint: disable=protected-access - cli._restore_webroot_config(config, renewalparams) + renew._restore_webroot_config(config, renewalparams) self.assertEqual(config.webroot_path, ["/var/www/"]) def test_renew_verb_empty_config(self): @@ -674,7 +674,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with open(os.path.join(rd, 'empty.conf'), 'w'): pass # leave the file empty args = ["renew", "--dry-run", "-tvv"] - self._test_renewal_common(False, [], args=args, renew=False, error_expected=True) + self._test_renewal_common(False, [], args=args, should_renew=False, error_expected=True) def _make_dummy_renewal_config(self): renewer_configs_dir = os.path.join(self.config_dir, 'renewal') @@ -695,7 +695,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_rc.return_value = mock_lineage with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: self._test_renewal_common(True, None, error_expected=error_expected, - args=['renew'], renew=False) + args=['renew'], should_renew=False) if assert_oc_called is not None: if assert_oc_called: self.assertTrue(mock_obtain_cert.called) @@ -751,13 +751,13 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: mock_obtain_cert.side_effect = Exception self._test_renewal_common(True, None, error_expected=True, - args=['renew'], renew=False) + args=['renew'], should_renew=False) def test_renew_with_bad_cli_args(self): self._test_renewal_common(True, None, args='renew -d example.com'.split(), - renew=False, error_expected=True) + should_renew=False, error_expected=True) self._test_renewal_common(True, None, args='renew --csr {0}'.format(CSR).split(), - renew=False, error_expected=True) + should_renew=False, error_expected=True) @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.main._treat_as_renewal') From 4ca25828b25c6f3ee3d5ce0591ccdbb4718f24e1 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 13:46:37 -0800 Subject: [PATCH 021/317] Get tests passing --- letsencrypt/cli.py | 1 - letsencrypt/plugins/disco.py | 1 + letsencrypt/tests/cli_test.py | 8 ++++---- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index e34bd971b..bea8b198d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1,5 +1,4 @@ """Let's Encrypt command CLI argument processing.""" -# pylint: disable=too-many-lines from __future__ import print_function import argparse import glob diff --git a/letsencrypt/plugins/disco.py b/letsencrypt/plugins/disco.py index 9ed6ac596..27d2fb541 100644 --- a/letsencrypt/plugins/disco.py +++ b/letsencrypt/plugins/disco.py @@ -4,6 +4,7 @@ import logging import pkg_resources import zope.interface +import zope.interface.verify from letsencrypt import constants from letsencrypt import errors diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index e79bcb95a..f1f539016 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -517,7 +517,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args += '-d foo.bar -a standalone certonly'.split() self._call(args) - @mock.patch('letsencrypt.cli.zope.component.getUtility') + @mock.patch('letsencrypt.main.zope.component.getUtility') def test_certonly_dry_run_new_request_success(self, mock_get_utility): mock_client = mock.MagicMock() mock_client.obtain_and_enroll_certificate.return_value = None @@ -530,7 +530,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(mock_get_utility().add_message.call_count, 1) @mock.patch('letsencrypt.crypto_util.notAfter') - @mock.patch('letsencrypt.cli.zope.component.getUtility') + @mock.patch('letsencrypt.main.zope.component.getUtility') def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter): cert_path = '/etc/letsencrypt/live/foo.bar' date = '1970-01-01' @@ -736,7 +736,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_renew_reconstitute_error(self): # pylint: disable=protected-access - with mock.patch('letsencrypt.cli._reconstitute') as mock_reconstitute: + with mock.patch('letsencrypt.main.renew._reconstitute') as mock_reconstitute: mock_reconstitute.side_effect = Exception self._test_renew_common(assert_oc_called=False, error_expected=True) @@ -759,7 +759,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._test_renewal_common(True, None, args='renew --csr {0}'.format(CSR).split(), should_renew=False, error_expected=True) - @mock.patch('letsencrypt.cli.zope.component.getUtility') + @mock.patch('letsencrypt.main.zope.component.getUtility') @mock.patch('letsencrypt.main._treat_as_renewal') @mock.patch('letsencrypt.main._init_le_client') def test_certonly_reinstall(self, mock_init, mock_renewal, mock_get_utility): From e644560a55087a5c0fbef7aff25d9c5ab7bec134 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 14:00:57 -0800 Subject: [PATCH 022/317] neaten --- letsencrypt/renew.py | 72 ++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/letsencrypt/renew.py b/letsencrypt/renew.py index 6eb3fa27c..bd00543c8 100644 --- a/letsencrypt/renew.py +++ b/letsencrypt/renew.py @@ -27,42 +27,6 @@ STR_CONFIG_ITEMS = ["config_dir", "logs_dir", "work_dir", "user_agent", INT_CONFIG_ITEMS = ["rsa_key_size", "tls_sni_01_port", "http01_port"] -def _renew_describe_results(config, renew_successes, renew_failures, - renew_skipped, parse_failures): - status = lambda x, msg: " " + "\n ".join(i + " (" + msg +")" for i in x) - if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates below have not been saved.)") - print() - if renew_skipped: - print("The following certs are not due for renewal yet:") - print(status(renew_skipped, "skipped")) - if not renew_successes and not renew_failures: - print("No renewals were attempted.") - elif renew_successes and not renew_failures: - print("Congratulations, all renewals succeeded. The following certs " - "have been renewed:") - print(status(renew_successes, "success")) - elif renew_failures and not renew_successes: - print("All renewal attempts failed. The following certs could not be " - "renewed:") - print(status(renew_failures, "failure")) - elif renew_failures and renew_successes: - print("The following certs were successfully renewed:") - print(status(renew_successes, "success")) - print("\nThe following certs could not be renewed:") - print(status(renew_failures, "failure")) - - if parse_failures: - print("\nAdditionally, the following renewal configuration files " - "were invalid: ") - print(status(parse_failures, "parsefail")) - - if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates above have not been saved.)") - - def renewal_conf_files(config): """Return /path/to/*.conf in the renewal conf directory""" return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf")) @@ -242,6 +206,42 @@ def should_renew(config, lineage): return False +def _renew_describe_results(config, renew_successes, renew_failures, + renew_skipped, parse_failures): + status = lambda ms, category: " " + "\n ".join(m + " (%s)" % category for m in ms) + if config.dry_run: + print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + print("** (The test certificates below have not been saved.)") + print() + if renew_skipped: + print("The following certs are not due for renewal yet:") + print(status(renew_skipped, "skipped")) + if not renew_successes and not renew_failures: + print("No renewals were attempted.") + elif renew_successes and not renew_failures: + print("Congratulations, all renewals succeeded. The following certs " + "have been renewed:") + print(status(renew_successes, "success")) + elif renew_failures and not renew_successes: + print("All renewal attempts failed. The following certs could not be " + "renewed:") + print(status(renew_failures, "failure")) + elif renew_failures and renew_successes: + print("The following certs were successfully renewed:") + print(status(renew_successes, "success")) + print("\nThe following certs could not be renewed:") + print(status(renew_failures, "failure")) + + if parse_failures: + print("\nAdditionally, the following renewal configuration files " + "were invalid: ") + print(status(parse_failures, "parsefail")) + + if config.dry_run: + print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + print("** (The test certificates above have not been saved.)") + + def renew(config, unused_plugins): """Renew previously-obtained certificates.""" From 6f7f036e2cac155b3bfddd614fb603bd7ce0ac9d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 14:07:35 -0800 Subject: [PATCH 023/317] Neaten further --- letsencrypt/renew.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/letsencrypt/renew.py b/letsencrypt/renew.py index bd00543c8..367eda5b0 100644 --- a/letsencrypt/renew.py +++ b/letsencrypt/renew.py @@ -208,34 +208,35 @@ def should_renew(config, lineage): def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): - status = lambda ms, category: " " + "\n ".join(m + " (%s)" % category for m in ms) + def _status(msgs, category): + return " " + "\n ".join("%s (%s)" % (m, category) for m in msgs) if config.dry_run: print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") print("** (The test certificates below have not been saved.)") print() if renew_skipped: print("The following certs are not due for renewal yet:") - print(status(renew_skipped, "skipped")) + print(_status(renew_skipped, "skipped")) if not renew_successes and not renew_failures: print("No renewals were attempted.") elif renew_successes and not renew_failures: print("Congratulations, all renewals succeeded. The following certs " "have been renewed:") - print(status(renew_successes, "success")) + print(_status(renew_successes, "success")) elif renew_failures and not renew_successes: print("All renewal attempts failed. The following certs could not be " "renewed:") - print(status(renew_failures, "failure")) + print(_status(renew_failures, "failure")) elif renew_failures and renew_successes: print("The following certs were successfully renewed:") - print(status(renew_successes, "success")) + print(_status(renew_successes, "success")) print("\nThe following certs could not be renewed:") - print(status(renew_failures, "failure")) + print(_status(renew_failures, "failure")) if parse_failures: print("\nAdditionally, the following renewal configuration files " "were invalid: ") - print(status(parse_failures, "parsefail")) + print(_status(parse_failures, "parsefail")) if config.dry_run: print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") From 50881cbb35d8e4a814691eb448ab3363878c01ae Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 14:49:12 -0800 Subject: [PATCH 024/317] Start splitting plugins.selection out of cli --- letsencrypt/cli.py | 152 ++----------------------------- letsencrypt/main.py | 9 +- letsencrypt/plugins/selection.py | 146 +++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 150 deletions(-) create mode 100644 letsencrypt/plugins/selection.py diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index bea8b198d..2b45b180d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -13,7 +13,6 @@ import configargparse import OpenSSL import six -import letsencrypt from letsencrypt import constants from letsencrypt import crypto_util @@ -23,6 +22,7 @@ from letsencrypt import le_util from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco +from letsencrypt.plugin.selection import cli_plugin_requests logger = logging.getLogger(__name__) @@ -33,9 +33,10 @@ helpful_parser = None # For help strings, figure out how the user ran us. # When invoked from letsencrypt-auto, sys.argv[0] is something like: # "/home/user/.local/share/letsencrypt/bin/letsencrypt" -# Note that this won't work if the user set VENV_PATH or XDG_DATA_HOME before running -# letsencrypt-auto (and sudo stops us from seeing if they did), so it should only be used -# for purposes where inability to detect letsencrypt-auto fails safely +# Note that this won't work if the user set VENV_PATH or XDG_DATA_HOME before +# running letsencrypt-auto (and sudo stops us from seeing if they did), so it +# should only be used for purposes where inability to detect letsencrypt-auto +# fails safely fragment = os.path.join(".local", "share", "letsencrypt") cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "letsencrypt" @@ -99,145 +100,6 @@ def usage_strings(plugins): return USAGE % (apache_doc, nginx_doc), SHORT_USAGE -def diagnose_configurator_problem(cfg_type, requested, plugins): - """ - Raise the most helpful error message about a plugin being unavailable - - :param str cfg_type: either "installer" or "authenticator" - :param str requested: the plugin that was requested - :param .PluginsRegistry plugins: available plugins - - :raises error.PluginSelectionError: if there was a problem - """ - - if requested: - if requested not in 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!r}" - .format(requested, plugins[requested].problem)) - elif cfg_type == "installer": - 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 "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 certonly" to get a cert you can install manually') - else: - msg = "{0} could not be determined or is not installed".format(cfg_type) - raise errors.PluginSelectionError(msg) - - -def set_configurator(previously, now): - """ - Setting configurators multiple ways is okay, as long as they all agree - :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 - return previously - if previously: - if previously != now: - msg = "Too many flags setting configurators/installers/authenticators {0} -> {1}" - raise errors.PluginSelectionError(msg.format(repr(previously), repr(now))) - return now - - -def cli_plugin_requests(config): - """ - Figure out which plugins the user requested with CLI and config options - - :returns: (requested authenticator string or None, requested installer string or None) - :rtype: tuple - """ - req_inst = req_auth = config.configurator - req_inst = set_configurator(req_inst, config.installer) - req_auth = set_configurator(req_auth, config.authenticator) - if config.nginx: - req_inst = set_configurator(req_inst, "nginx") - req_auth = set_configurator(req_auth, "nginx") - if config.apache: - req_inst = set_configurator(req_inst, "apache") - req_auth = set_configurator(req_auth, "apache") - if config.standalone: - req_auth = set_configurator(req_auth, "standalone") - if config.webroot: - req_auth = set_configurator(req_auth, "webroot") - if config.manual: - req_auth = set_configurator(req_auth, "manual") - logger.debug("Requested authenticator %s and installer %s", req_auth, req_inst) - return req_auth, req_inst - - -noninstaller_plugins = ["webroot", "manual", "standalone"] - - -def choose_configurator_plugins(config, plugins, verb): - """ - Figure out which configurator we're going to use, modifies - config.authenticator and config.istaller strings to reflect that choice if - necessary. - - :raises errors.PluginSelectionError if there was a problem - - :returns: (an `IAuthenticator` or None, an `IInstaller` or None) - :rtype: tuple - """ - - req_auth, req_inst = cli_plugin_requests(config) - - # Which plugins do we need? - if verb == "run": - need_inst = need_auth = True - if req_auth in noninstaller_plugins and not req_inst: - msg = ('With the {0} plugin, you probably want to use the "certonly" command, eg:{1}' - '{1} {2} certonly --{0}{1}{1}' - '(Alternatively, add a --installer flag. See https://eff.org/letsencrypt-plugins' - '{1} and "--help plugins" for more information.)'.format( - req_auth, os.linesep, cli_command)) - - raise errors.MissingCommandlineFlag(msg) - else: - need_inst = need_auth = False - if verb == "certonly": - need_auth = True - if verb == "install": - need_inst = True - if config.authenticator: - logger.warn("Specifying an authenticator doesn't make sense in install mode") - - # Try to meet the user's request and/or ask them to pick plugins - authenticator = installer = None - if verb == "run" and req_auth == req_inst: - # Unless the user has explicitly asked for different auth/install, - # only consider offering a single choice - authenticator = installer = display_ops.pick_configurator(config, req_inst, plugins) - else: - if need_inst or req_inst: - installer = display_ops.pick_installer(config, req_inst, plugins) - if need_auth: - authenticator = display_ops.pick_authenticator(config, req_auth, plugins) - logger.debug("Selected authenticator %s and installer %s", authenticator, installer) - - # Report on any failures - if need_inst and not installer: - diagnose_configurator_problem("installer", req_inst, plugins) - if need_auth and not authenticator: - diagnose_configurator_problem("authenticator", req_auth, plugins) - - record_chosen_plugins(config, plugins, authenticator, installer) - return installer, authenticator - - -def record_chosen_plugins(config, plugins, auth, inst): - "Update the config entries to reflect the plugins we actually selected." - cn = config.namespace - cn.authenticator = plugins.find_init(auth).name if auth else "none" - cn.installer = plugins.find_init(inst).name if inst else "none" def set_by_cli(var): @@ -256,7 +118,7 @@ def set_by_cli(var): detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator - auth, inst = cli_plugin_requests(detector) + auth, inst = plugin_selection.cli_plugin_requests(detector) detector.authenticator = auth if auth else "" detector.installer = inst if inst else "" logger.debug("Default Detector is %r", detector) @@ -308,7 +170,7 @@ def flag_default(name): def config_help(name, hidden=False): - """Help message for `.IConfig` attribute.""" + """Extract the help message for an `.IConfig` attribute.""" if hidden: return argparse.SUPPRESS else: diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 8d59993df..48e6e8505 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -6,8 +6,6 @@ import os import sys import zope.component -import letsencrypt - from letsencrypt import account from letsencrypt import client from letsencrypt import cli @@ -25,6 +23,7 @@ from letsencrypt import storage from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco +from letsencrypt.plugins.selection import choose_configurator_plugins import traceback import logging.handlers @@ -404,7 +403,7 @@ def install(config, plugins): # this function ... try: - installer, _ = cli.choose_configurator_plugins(config, plugins, "install") + installer, _ = choose_configurator_plugins(config, plugins, "install") except errors.PluginSelectionError as e: return e.message @@ -479,7 +478,7 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals # TODO: Make run as close to auth + install as possible # Possible difficulties: config.csr was hacked into auth try: - installer, authenticator = cli.choose_configurator_plugins(config, plugins, "run") + installer, authenticator = choose_configurator_plugins(config, plugins, "run") except errors.PluginSelectionError as e: return e.message @@ -509,7 +508,7 @@ def obtain_cert(config, plugins, lineage=None): # pylint: disable=too-many-locals try: # installers are used in auth mode to determine domain names - installer, authenticator = cli.choose_configurator_plugins(config, plugins, "certonly") + installer, authenticator = choose_configurator_plugins(config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise diff --git a/letsencrypt/plugins/selection.py b/letsencrypt/plugins/selection.py new file mode 100644 index 000000000..fc7274a24 --- /dev/null +++ b/letsencrypt/plugins/selection.py @@ -0,0 +1,146 @@ +from __future__ import print_function +import os +from letsencrypt import errors +from letsencrypt.display import ops as display_ops + +logger = logging.getLogger(__name__) + +noninstaller_plugins = ["webroot", "manual", "standalone"] + +def record_chosen_plugins(config, plugins, auth, inst): + "Update the config entries to reflect the plugins we actually selected." + cn = config.namespace + cn.authenticator = plugins.find_init(auth).name if auth else "none" + cn.installer = plugins.find_init(inst).name if inst else "none" + + +def choose_configurator_plugins(config, plugins, verb): + """ + Figure out which configurator we're going to use, modifies + config.authenticator and config.istaller strings to reflect that choice if + necessary. + + :raises errors.PluginSelectionError if there was a problem + + :returns: (an `IAuthenticator` or None, an `IInstaller` or None) + :rtype: tuple + """ + + req_auth, req_inst = cli_plugin_requests(config) + + # Which plugins do we need? + if verb == "run": + need_inst = need_auth = True + from letsencrypt.cli import cli_command + if req_auth in noninstaller_plugins and not req_inst: + msg = ('With the {0} plugin, you probably want to use the "certonly" command, eg:{1}' + '{1} {2} certonly --{0}{1}{1}' + '(Alternatively, add a --installer flag. See https://eff.org/letsencrypt-plugins' + '{1} and "--help plugins" for more information.)'.format( + req_auth, os.linesep, cli_command)) + + raise errors.MissingCommandlineFlag(msg) + else: + need_inst = need_auth = False + if verb == "certonly": + need_auth = True + if verb == "install": + need_inst = True + if config.authenticator: + logger.warn("Specifying an authenticator doesn't make sense in install mode") + + # Try to meet the user's request and/or ask them to pick plugins + authenticator = installer = None + if verb == "run" and req_auth == req_inst: + # Unless the user has explicitly asked for different auth/install, + # only consider offering a single choice + authenticator = installer = display_ops.pick_configurator(config, req_inst, plugins) + else: + if need_inst or req_inst: + installer = display_ops.pick_installer(config, req_inst, plugins) + if need_auth: + authenticator = display_ops.pick_authenticator(config, req_auth, plugins) + logger.debug("Selected authenticator %s and installer %s", authenticator, installer) + + # Report on any failures + if need_inst and not installer: + diagnose_configurator_problem("installer", req_inst, plugins) + if need_auth and not authenticator: + diagnose_configurator_problem("authenticator", req_auth, plugins) + + record_chosen_plugins(config, plugins, authenticator, installer) + return installer, authenticator + + +def set_configurator(previously, now): + """ + Setting configurators multiple ways is okay, as long as they all agree + :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 + return previously + if previously: + if previously != now: + msg = "Too many flags setting configurators/installers/authenticators {0} -> {1}" + raise errors.PluginSelectionError(msg.format(repr(previously), repr(now))) + return now + + +def cli_plugin_requests(config): + """ + Figure out which plugins the user requested with CLI and config options + + :returns: (requested authenticator string or None, requested installer string or None) + :rtype: tuple + """ + req_inst = req_auth = config.configurator + req_inst = set_configurator(req_inst, config.installer) + req_auth = set_configurator(req_auth, config.authenticator) + if config.nginx: + req_inst = set_configurator(req_inst, "nginx") + req_auth = set_configurator(req_auth, "nginx") + if config.apache: + req_inst = set_configurator(req_inst, "apache") + req_auth = set_configurator(req_auth, "apache") + if config.standalone: + req_auth = set_configurator(req_auth, "standalone") + if config.webroot: + req_auth = set_configurator(req_auth, "webroot") + if config.manual: + req_auth = set_configurator(req_auth, "manual") + logger.debug("Requested authenticator %s and installer %s", req_auth, req_inst) + return req_auth, req_inst + + +def diagnose_configurator_problem(cfg_type, requested, plugins): + """ + Raise the most helpful error message about a plugin being unavailable + + :param str cfg_type: either "installer" or "authenticator" + :param str requested: the plugin that was requested + :param .PluginsRegistry plugins: available plugins + + :raises error.PluginSelectionError: if there was a problem + """ + + if requested: + if requested not in 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!r}" + .format(requested, plugins[requested].problem)) + elif cfg_type == "installer": + 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 "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 certonly" to get a cert you can install manually') + else: + msg = "{0} could not be determined or is not installed".format(cfg_type) + raise errors.PluginSelectionError(msg) From 86ab35df4f66dfc15a99cf0c581b9100b77cc643 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 15:07:13 -0800 Subject: [PATCH 025/317] Move argparse type extraction back into cli.py --- letsencrypt/cli.py | 14 ++++++++++++++ letsencrypt/renew.py | 13 +++---------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index bea8b198d..017e0a62e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -283,6 +283,16 @@ def set_by_cli(var): # static housekeeping var set_by_cli.detector = None + +def argparse_type(variable): + "Return our argparse type function for a config variable (default: str)" + # pylint: disable=protected-access + for action in helpful_parser.parser._actions: + if action.type is not None and action.dest == variable: + return action.type + return str + + def read_file(filename, mode="rb"): """Returns the given file's contents. @@ -304,6 +314,10 @@ def read_file(filename, mode="rb"): def flag_default(name): """Default value for CLI flag.""" + # XXX: this is an internal housekeeping notion of defaults before + # argparse has been set up; it is not accurate for all flags. Call it + # with caution. Plugin defaults are missing, and some things are using + # defaults defined in this file, not in constants.py :( return constants.CLI_DEFAULTS[name] diff --git a/letsencrypt/renew.py b/letsencrypt/renew.py index 367eda5b0..9dc1ff4df 100644 --- a/letsencrypt/renew.py +++ b/letsencrypt/renew.py @@ -139,21 +139,14 @@ def _restore_plugin_configs(config, renewalparams): for config_item, config_value in six.iteritems(renewalparams): if config_item.startswith(plugin_prefix + "_") and not cli.set_by_cli(config_item): # Values None, True, and False need to be treated specially, - # As they don't get parsed correctly based on type + # As their types aren't handled correctly by configobj if config_value in ("None", "True", "False"): # bool("False") == True # pylint: disable=eval-used setattr(config.namespace, config_item, eval(config_value)) - continue - # If argparse has a type for this variable, use it: - # pylint: disable=protected-access - for action in cli.helpful_parser.parser._actions: - if action.type is not None and action.dest == config_item: - setattr(config.namespace, config_item, - action.type(config_value)) - break else: - setattr(config.namespace, config_item, str(config_value)) + cast = cli.argparse_type(config_item) + setattr(config.namespace, config_item, cast(config_value)) def _restore_required_config_elements(config, renewalparams): From c6fece8b404e5662772484d394490bf7ff3444b4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 15:24:57 -0800 Subject: [PATCH 026/317] Also move plugin selection logic from display.ops --- letsencrypt/client.py | 5 +- letsencrypt/display/ops.py | 136 ++------------------------ letsencrypt/plugins/selection.py | 131 ++++++++++++++++++++++++- letsencrypt/tests/display/ops_test.py | 12 +-- 4 files changed, 143 insertions(+), 141 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 6134c4e6e..1655c1fa5 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -11,8 +11,6 @@ from acme import client as acme_client from acme import jose from acme import messages -import letsencrypt - from letsencrypt import account from letsencrypt import auth_handler from letsencrypt import configuration @@ -27,6 +25,7 @@ from letsencrypt import storage from letsencrypt.display import ops as display_ops from letsencrypt.display import enhancements +from letsencrypt.plugins import selection as plugin_selection logger = logging.getLogger(__name__) @@ -524,7 +523,7 @@ def rollback(default_installer, checkpoints, config, plugins): """ # Misconfigurations are only a slight problems... allow the user to rollback - installer = display_ops.pick_installer( + installer = plugin_selection.pick_installer( config, default_installer, plugins, question="Which installer " "should be used for rollback?") diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index f0dec8b06..d60073ce0 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -8,131 +8,13 @@ from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt.display import util as display_util +import letsencrypt.plugins.selection logger = logging.getLogger(__name__) # Define a helper function to avoid verbose code -util = zope.component.getUtility - - -def choose_plugin(prepared, question): - """Allow the user to choose their plugin. - - :param list prepared: List of `~.PluginEntryPoint`. - :param str question: Question to be presented to the user. - - :returns: Plugin entry point chosen by the user. - :rtype: `~.PluginEntryPoint` - - """ - opts = [plugin_ep.description_with_name + - (" [Misconfigured]" if plugin_ep.misconfigured else "") - for plugin_ep in prepared] - - while True: - disp = util(interfaces.IDisplay) - code, index = disp.menu(question, opts, help_label="More Info") - - if code == display_util.OK: - plugin_ep = prepared[index] - if plugin_ep.misconfigured: - util(interfaces.IDisplay).notification( - "The selected plugin encountered an error while parsing " - "your server configuration and cannot be used. The error " - "was:\n\n{0}".format(plugin_ep.prepare()), - height=display_util.HEIGHT, pause=False) - else: - return plugin_ep - elif code == display_util.HELP: - if prepared[index].misconfigured: - msg = "Reported Error: %s" % prepared[index].prepare() - else: - msg = prepared[index].init().more_info() - util(interfaces.IDisplay).notification( - msg, height=display_util.HEIGHT) - else: - return None - - -def pick_plugin(config, default, plugins, question, ifaces): - """Pick plugin. - - :param letsencrypt.interfaces.IConfig: Configuration - :param str default: Plugin name supplied by user or ``None``. - :param letsencrypt.plugins.disco.PluginsRegistry plugins: - All plugins registered as entry points. - :param str question: Question to be presented to the user in case - multiple candidates are found. - :param list ifaces: Interfaces that plugins must provide. - - :returns: Initialized plugin. - :rtype: IPlugin - - """ - if default is not None: - # throw more UX-friendly error if default not in plugins - filtered = plugins.filter(lambda p_ep: p_ep.name == default) - else: - if config.noninteractive_mode: - # it's really bad to auto-select the single available plugin in - # non-interactive mode, because an update could later add a second - # available plugin - raise errors.MissingCommandlineFlag( - "Missing command line flags. For non-interactive execution, " - "you will need to specify a plugin on the command line. Run " - "with '--help plugins' to see a list of options, and see " - "https://eff.org/letsencrypt-plugins for more detail on what " - "the plugins do and how to use them.") - - filtered = plugins.visible().ifaces(ifaces) - - filtered.init(config) - verified = filtered.verify(ifaces) - verified.prepare() - prepared = verified.available() - - if len(prepared) > 1: - logger.debug("Multiple candidate plugins: %s", prepared) - plugin_ep = choose_plugin(prepared.values(), question) - if plugin_ep is None: - return None - else: - return plugin_ep.init() - elif len(prepared) == 1: - plugin_ep = prepared.values()[0] - logger.debug("Single candidate plugin: %s", plugin_ep) - if plugin_ep.misconfigured: - return None - return plugin_ep.init() - else: - logger.debug("No candidate plugin") - return None - - -def pick_authenticator( - config, default, plugins, question="How would you " - "like to authenticate with the Let's Encrypt CA?"): - """Pick authentication plugin.""" - return pick_plugin( - config, default, plugins, question, (interfaces.IAuthenticator,)) - - -def pick_installer(config, default, plugins, - question="How would you like to install certificates?"): - """Pick installer plugin.""" - return pick_plugin( - config, default, plugins, question, (interfaces.IInstaller,)) - - -def pick_configurator( - config, default, plugins, - question="How would you like to authenticate and install " - "certificates?"): - """Pick configurator plugin.""" - return pick_plugin( - config, default, plugins, question, - (interfaces.IAuthenticator, interfaces.IInstaller)) +z_util = zope.component.getUtility def get_email(more=False, invalid=False): @@ -182,7 +64,7 @@ def choose_account(accounts): # Note this will get more complicated once we start recording authorizations labels = [acc.slug for acc in accounts] - code, index = util(interfaces.IDisplay).menu( + code, index = z_util(interfaces.IDisplay).menu( "Please choose an account", labels) if code == display_util.OK: return accounts[index] @@ -208,7 +90,7 @@ def choose_names(installer): names = get_valid_domains(domains) if not names: - manual = util(interfaces.IDisplay).yesno( + manual = z_util(interfaces.IDisplay).yesno( "No names were found in your configuration files.{0}You should " "specify ServerNames in your config files in order to allow for " "accurate installation of your certificate.{0}" @@ -256,7 +138,7 @@ def _filter_names(names): :rtype: tuple """ - code, names = util(interfaces.IDisplay).checklist( + code, names = z_util(interfaces.IDisplay).checklist( "Which names would you like to activate HTTPS for?", tags=names, cli_flag="--domains") return code, [str(s) for s in names] @@ -265,7 +147,7 @@ def _filter_names(names): def _choose_names_manually(): """Manually input names for those without an installer.""" - code, input_ = util(interfaces.IDisplay).input( + code, input_ = z_util(interfaces.IDisplay).input( "Please enter in your domain name(s) (comma and/or space separated) ", cli_flag="--domains") @@ -300,7 +182,7 @@ def _choose_names_manually(): if retry_message: # We had error in input - retry = util(interfaces.IDisplay).yesno(retry_message) + retry = z_util(interfaces.IDisplay).yesno(retry_message) if retry: return _choose_names_manually() else: @@ -316,7 +198,7 @@ def success_installation(domains): :param list domains: domain names which were enabled """ - util(interfaces.IDisplay).notification( + z_util(interfaces.IDisplay).notification( "Congratulations! You have successfully enabled {0}{1}{1}" "You should test your configuration at:{1}{2}".format( _gen_https_names(domains), @@ -335,7 +217,7 @@ def success_renewal(domains, action): :param str action: can be "reinstall" or "renew" """ - util(interfaces.IDisplay).notification( + z_util(interfaces.IDisplay).notification( "Your existing certificate has been successfully {3}ed, and the " "new certificate has been installed.{1}{1}" "The new certificate covers the following domains: {0}{1}{1}" diff --git a/letsencrypt/plugins/selection.py b/letsencrypt/plugins/selection.py index fc7274a24..bf6187950 100644 --- a/letsencrypt/plugins/selection.py +++ b/letsencrypt/plugins/selection.py @@ -1,7 +1,128 @@ from __future__ import print_function import os -from letsencrypt import errors -from letsencrypt.display import ops as display_ops +from letsencrypt import errors, interfaces +from letsencrypt.display import util as display_util + +logger = logging.getLogger(__name__) +z_util = zope.component.getUtility + +def pick_configurator( + config, default, plugins, + question="How would you like to authenticate and install " + "certificates?"): + """Pick configurator plugin.""" + return pick_plugin( + config, default, plugins, question, + (interfaces.IAuthenticator, interfaces.IInstaller)) + + +def pick_installer(config, default, plugins, + question="How would you like to install certificates?"): + """Pick installer plugin.""" + return pick_plugin( + config, default, plugins, question, (interfaces.IInstaller,)) + + +def pick_authenticator( + config, default, plugins, question="How would you " + "like to authenticate with the Let's Encrypt CA?"): + """Pick authentication plugin.""" + return pick_plugin( + config, default, plugins, question, (interfaces.IAuthenticator,)) + + +def pick_plugin(config, default, plugins, question, ifaces): + """Pick plugin. + + :param letsencrypt.interfaces.IConfig: Configuration + :param str default: Plugin name supplied by user or ``None``. + :param letsencrypt.plugins.disco.PluginsRegistry plugins: + All plugins registered as entry points. + :param str question: Question to be presented to the user in case + multiple candidates are found. + :param list ifaces: Interfaces that plugins must provide. + + :returns: Initialized plugin. + :rtype: IPlugin + + """ + if default is not None: + # throw more UX-friendly error if default not in plugins + filtered = plugins.filter(lambda p_ep: p_ep.name == default) + else: + if config.noninteractive_mode: + # it's really bad to auto-select the single available plugin in + # non-interactive mode, because an update could later add a second + # available plugin + raise errors.MissingCommandlineFlag( + "Missing command line flags. For non-interactive execution, " + "you will need to specify a plugin on the command line. Run " + "with '--help plugins' to see a list of options, and see " + "https://eff.org/letsencrypt-plugins for more detail on what " + "the plugins do and how to use them.") + + filtered = plugins.visible().ifaces(ifaces) + + filtered.init(config) + verified = filtered.verify(ifaces) + verified.prepare() + prepared = verified.available() + + if len(prepared) > 1: + logger.debug("Multiple candidate plugins: %s", prepared) + plugin_ep = choose_plugin(prepared.values(), question) + if plugin_ep is None: + return None + else: + return plugin_ep.init() + elif len(prepared) == 1: + plugin_ep = prepared.values()[0] + logger.debug("Single candidate plugin: %s", plugin_ep) + if plugin_ep.misconfigured: + return None + return plugin_ep.init() + else: + logger.debug("No candidate plugin") + return None + + +def choose_plugin(prepared, question): + """Allow the user to choose their plugin. + + :param list prepared: List of `~.PluginEntryPoint`. + :param str question: Question to be presented to the user. + + :returns: Plugin entry point chosen by the user. + :rtype: `~.PluginEntryPoint` + + """ + opts = [plugin_ep.description_with_name + + (" [Misconfigured]" if plugin_ep.misconfigured else "") + for plugin_ep in prepared] + + while True: + disp = z_util(interfaces.IDisplay) + code, index = disp.menu(question, opts, help_label="More Info") + + if code == display_util.OK: + plugin_ep = prepared[index] + if plugin_ep.misconfigured: + z_util(interfaces.IDisplay).notification( + "The selected plugin encountered an error while parsing " + "your server configuration and cannot be used. The error " + "was:\n\n{0}".format(plugin_ep.prepare()), + height=display_util.HEIGHT, pause=False) + else: + return plugin_ep + elif code == display_util.HELP: + if prepared[index].misconfigured: + msg = "Reported Error: %s" % prepared[index].prepare() + else: + msg = prepared[index].init().more_info() + z_util(interfaces.IDisplay).notification( + msg, height=display_util.HEIGHT) + else: + return None logger = logging.getLogger(__name__) @@ -54,12 +175,12 @@ def choose_configurator_plugins(config, plugins, verb): if verb == "run" and req_auth == req_inst: # Unless the user has explicitly asked for different auth/install, # only consider offering a single choice - authenticator = installer = display_ops.pick_configurator(config, req_inst, plugins) + authenticator = installer = pick_configurator(config, req_inst, plugins) else: if need_inst or req_inst: - installer = display_ops.pick_installer(config, req_inst, plugins) + installer = pick_installer(config, req_inst, plugins) if need_auth: - authenticator = display_ops.pick_authenticator(config, req_auth, plugins) + authenticator = pick_authenticator(config, req_auth, plugins) logger.debug("Selected authenticator %s and installer %s", authenticator, installer) # Report on any failures diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index 9a39e1538..a7e8e1d74 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -76,7 +76,7 @@ class PickPluginTest(unittest.TestCase): self.ifaces = [] def _call(self): - from letsencrypt.display.ops import pick_plugin + from letsencrypt.plugins.selection import pick_plugin return pick_plugin(self.config, self.default, self.reg, self.question, self.ifaces) @@ -149,17 +149,17 @@ class ConveniencePickPluginTest(unittest.TestCase): config, default, plugins, "Question?", ifaces) def test_authenticator(self): - from letsencrypt.display.ops import pick_authenticator + from letsencrypt.plugins.selection import pick_authenticator self._test(pick_authenticator, (interfaces.IAuthenticator,)) def test_installer(self): - from letsencrypt.display.ops import pick_installer + from letsencrypt.plugins.selection import pick_installer self._test(pick_installer, (interfaces.IInstaller,)) def test_configurator(self): - from letsencrypt.display.ops import pick_configurator - self._test(pick_configurator, ( - interfaces.IAuthenticator, interfaces.IInstaller)) + from letsencrypt.plugins.selection import pick_configurator + self._test(pick_configurator, + (interfaces.IAuthenticator, interfaces.IInstaller)) class GetEmailTest(unittest.TestCase): From 1c652716a25e0369fe5c3088b50a7c6032eb42a4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 15:37:24 -0800 Subject: [PATCH 027/317] Start splitting out tests for plugins.selection --- letsencrypt/cli.py | 6 +- letsencrypt/client.py | 2 + letsencrypt/display/ops.py | 2 - letsencrypt/main.py | 2 + letsencrypt/plugins/selection.py | 10 +- letsencrypt/plugins/selection_test.py | 149 ++++++++++++++++++++++++++ letsencrypt/tests/display/ops_test.py | 140 ------------------------ 7 files changed, 165 insertions(+), 146 deletions(-) create mode 100644 letsencrypt/plugins/selection_test.py diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 4788a41e5..cb6f66be9 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -13,6 +13,7 @@ import configargparse import OpenSSL import six +import letsencrypt from letsencrypt import constants from letsencrypt import crypto_util @@ -20,9 +21,8 @@ from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util -from letsencrypt.display import ops as display_ops from letsencrypt.plugins import disco as plugins_disco -from letsencrypt.plugin.selection import cli_plugin_requests +from letsencrypt.plugins.selection import cli_plugin_requests logger = logging.getLogger(__name__) @@ -118,7 +118,7 @@ def set_by_cli(var): detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator - auth, inst = plugin_selection.cli_plugin_requests(detector) + auth, inst = cli_plugin_requests(detector) detector.authenticator = auth if auth else "" detector.installer = inst if inst else "" logger.debug("Default Detector is %r", detector) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 1655c1fa5..d559b6311 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -11,6 +11,8 @@ from acme import client as acme_client from acme import jose from acme import messages +import letsencrypt + from letsencrypt import account from letsencrypt import auth_handler from letsencrypt import configuration diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index d60073ce0..302051b1b 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -8,8 +8,6 @@ from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt.display import util as display_util -import letsencrypt.plugins.selection - logger = logging.getLogger(__name__) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 48e6e8505..153e118a4 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -6,6 +6,8 @@ import os import sys import zope.component +import letsencrypt + from letsencrypt import account from letsencrypt import client from letsencrypt import cli diff --git a/letsencrypt/plugins/selection.py b/letsencrypt/plugins/selection.py index bf6187950..9c5a7804a 100644 --- a/letsencrypt/plugins/selection.py +++ b/letsencrypt/plugins/selection.py @@ -1,6 +1,14 @@ +"""Decide which plugins to use for authentication & installation""" from __future__ import print_function + import os -from letsencrypt import errors, interfaces +import logging + +import zope.component + +from letsencrypt import errors +from letsencrypt import interfaces + from letsencrypt.display import util as display_util logger = logging.getLogger(__name__) diff --git a/letsencrypt/plugins/selection_test.py b/letsencrypt/plugins/selection_test.py new file mode 100644 index 000000000..0beaab076 --- /dev/null +++ b/letsencrypt/plugins/selection_test.py @@ -0,0 +1,149 @@ +"""Tests for letsenecrypt.plugins.selection""" +import sys +import unittest + +import mock +import zope.component + +from letsencrypt.display import util as display_util +from letsencrypt import interfaces + + +class ConveniencePickPluginTest(unittest.TestCase): + """Tests for letsencrypt.plugins.selection.pick_*.""" + + def _test(self, fun, ifaces): + config = mock.Mock() + default = mock.Mock() + plugins = mock.Mock() + + with mock.patch("letsencrypt.plugins.selection.pick_plugin") as mock_p: + mock_p.return_value = "foo" + self.assertEqual("foo", fun(config, default, plugins, "Question?")) + mock_p.assert_called_once_with( + config, default, plugins, "Question?", ifaces) + + def test_authenticator(self): + from letsencrypt.plugins.selection import pick_authenticator + self._test(pick_authenticator, (interfaces.IAuthenticator,)) + + def test_installer(self): + from letsencrypt.plugins.selection import pick_installer + self._test(pick_installer, (interfaces.IInstaller,)) + + def test_configurator(self): + from letsencrypt.plugins.selection import pick_configurator + self._test(pick_configurator, + (interfaces.IAuthenticator, interfaces.IInstaller)) + + +class PickPluginTest(unittest.TestCase): + """Tests for letsencrypt.plugins.selection.pick_plugin.""" + + def setUp(self): + self.config = mock.Mock(noninteractive_mode=False) + self.default = None + self.reg = mock.MagicMock() + self.question = "Question?" + self.ifaces = [] + + def _call(self): + from letsencrypt.plugins.selection import pick_plugin + return pick_plugin(self.config, self.default, self.reg, + self.question, self.ifaces) + + def test_default_provided(self): + self.default = "foo" + self._call() + self.assertEqual(1, self.reg.filter.call_count) + + def test_no_default(self): + self._call() + self.assertEqual(1, self.reg.visible().ifaces.call_count) + + def test_no_candidate(self): + self.assertTrue(self._call() is None) + + def test_single(self): + plugin_ep = mock.MagicMock() + plugin_ep.init.return_value = "foo" + plugin_ep.misconfigured = False + + self.reg.visible().ifaces().verify().available.return_value = { + "bar": plugin_ep} + self.assertEqual("foo", self._call()) + + def test_single_misconfigured(self): + plugin_ep = mock.MagicMock() + plugin_ep.init.return_value = "foo" + plugin_ep.misconfigured = True + + self.reg.visible().ifaces().verify().available.return_value = { + "bar": plugin_ep} + self.assertTrue(self._call() is None) + + def test_multiple(self): + plugin_ep = mock.MagicMock() + plugin_ep.init.return_value = "foo" + self.reg.visible().ifaces().verify().available.return_value = { + "bar": plugin_ep, + "baz": plugin_ep, + } + with mock.patch("letsencrypt.plugins.selection.choose_plugin") as mock_choose: + mock_choose.return_value = plugin_ep + self.assertEqual("foo", self._call()) + mock_choose.assert_called_once_with( + [plugin_ep, plugin_ep], self.question) + + def test_choose_plugin_none(self): + self.reg.visible().ifaces().verify().available.return_value = { + "bar": None, + "baz": None, + } + + with mock.patch("letsencrypt.plugins.selection.choose_plugin") as mock_choose: + mock_choose.return_value = None + self.assertTrue(self._call() is None) + + +class ChoosePluginTest(unittest.TestCase): + """Tests for letsencrypt.plugins.selection.choose_plugin.""" + + def setUp(self): + zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) + self.mock_apache = mock.Mock( + description_with_name="a", misconfigured=True) + self.mock_stand = mock.Mock( + description_with_name="s", misconfigured=False) + self.mock_stand.init().more_info.return_value = "standalone" + self.plugins = [ + self.mock_apache, + self.mock_stand, + ] + + def _call(self): + from letsencrypt.plugins.selection import choose_plugin + return choose_plugin(self.plugins, "Question?") + + @mock.patch("letsencrypt.plugins.selection.z_util") + def test_selection(self, mock_util): + mock_util().menu.side_effect = [(display_util.OK, 0), + (display_util.OK, 1)] + self.assertEqual(self.mock_stand, self._call()) + self.assertEqual(mock_util().notification.call_count, 1) + + @mock.patch("letsencrypt.plugins.selection.z_util") + def test_more_info(self, mock_util): + mock_util().menu.side_effect = [ + (display_util.HELP, 0), + (display_util.HELP, 1), + (display_util.OK, 1), + ] + + self.assertEqual(self.mock_stand, self._call()) + self.assertEqual(mock_util().notification.call_count, 2) + + @mock.patch("letsencrypt.plugins.selection.z_util") + def test_no_choice(self, mock_util): + mock_util().menu.return_value = (display_util.CANCEL, 0) + self.assertTrue(self._call() is None) diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index a7e8e1d74..8a52c872b 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -22,146 +22,6 @@ from letsencrypt.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) -class ChoosePluginTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.choose_plugin.""" - - def setUp(self): - zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) - self.mock_apache = mock.Mock( - description_with_name="a", misconfigured=True) - self.mock_stand = mock.Mock( - description_with_name="s", misconfigured=False) - self.mock_stand.init().more_info.return_value = "standalone" - self.plugins = [ - self.mock_apache, - self.mock_stand, - ] - - def _call(self): - from letsencrypt.display.ops import choose_plugin - return choose_plugin(self.plugins, "Question?") - - @mock.patch("letsencrypt.display.ops.util") - def test_selection(self, mock_util): - mock_util().menu.side_effect = [(display_util.OK, 0), - (display_util.OK, 1)] - self.assertEqual(self.mock_stand, self._call()) - self.assertEqual(mock_util().notification.call_count, 1) - - @mock.patch("letsencrypt.display.ops.util") - def test_more_info(self, mock_util): - mock_util().menu.side_effect = [ - (display_util.HELP, 0), - (display_util.HELP, 1), - (display_util.OK, 1), - ] - - self.assertEqual(self.mock_stand, self._call()) - self.assertEqual(mock_util().notification.call_count, 2) - - @mock.patch("letsencrypt.display.ops.util") - def test_no_choice(self, mock_util): - mock_util().menu.return_value = (display_util.CANCEL, 0) - self.assertTrue(self._call() is None) - - -class PickPluginTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.pick_plugin.""" - - def setUp(self): - self.config = mock.Mock(noninteractive_mode=False) - self.default = None - self.reg = mock.MagicMock() - self.question = "Question?" - self.ifaces = [] - - def _call(self): - from letsencrypt.plugins.selection import pick_plugin - return pick_plugin(self.config, self.default, self.reg, - self.question, self.ifaces) - - def test_default_provided(self): - self.default = "foo" - self._call() - self.assertEqual(1, self.reg.filter.call_count) - - def test_no_default(self): - self._call() - self.assertEqual(1, self.reg.visible().ifaces.call_count) - - def test_no_candidate(self): - self.assertTrue(self._call() is None) - - def test_single(self): - plugin_ep = mock.MagicMock() - plugin_ep.init.return_value = "foo" - plugin_ep.misconfigured = False - - self.reg.visible().ifaces().verify().available.return_value = { - "bar": plugin_ep} - self.assertEqual("foo", self._call()) - - def test_single_misconfigured(self): - plugin_ep = mock.MagicMock() - plugin_ep.init.return_value = "foo" - plugin_ep.misconfigured = True - - self.reg.visible().ifaces().verify().available.return_value = { - "bar": plugin_ep} - self.assertTrue(self._call() is None) - - def test_multiple(self): - plugin_ep = mock.MagicMock() - plugin_ep.init.return_value = "foo" - self.reg.visible().ifaces().verify().available.return_value = { - "bar": plugin_ep, - "baz": plugin_ep, - } - with mock.patch("letsencrypt.display.ops.choose_plugin") as mock_choose: - mock_choose.return_value = plugin_ep - self.assertEqual("foo", self._call()) - mock_choose.assert_called_once_with( - [plugin_ep, plugin_ep], self.question) - - def test_choose_plugin_none(self): - self.reg.visible().ifaces().verify().available.return_value = { - "bar": None, - "baz": None, - } - - with mock.patch("letsencrypt.display.ops.choose_plugin") as mock_choose: - mock_choose.return_value = None - self.assertTrue(self._call() is None) - - -class ConveniencePickPluginTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.pick_*.""" - - def _test(self, fun, ifaces): - config = mock.Mock() - default = mock.Mock() - plugins = mock.Mock() - - with mock.patch("letsencrypt.display.ops.pick_plugin") as mock_p: - mock_p.return_value = "foo" - self.assertEqual("foo", fun(config, default, plugins, "Question?")) - mock_p.assert_called_once_with( - config, default, plugins, "Question?", ifaces) - - def test_authenticator(self): - from letsencrypt.plugins.selection import pick_authenticator - self._test(pick_authenticator, (interfaces.IAuthenticator,)) - - def test_installer(self): - from letsencrypt.plugins.selection import pick_installer - self._test(pick_installer, (interfaces.IInstaller,)) - - def test_configurator(self): - from letsencrypt.plugins.selection import pick_configurator - self._test(pick_configurator, - (interfaces.IAuthenticator, interfaces.IInstaller)) - - class GetEmailTest(unittest.TestCase): """Tests for letsencrypt.display.ops.get_email.""" From 6e9d2b7116c0dcc6cb327c7fd3180b0bf78dac47 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 15:59:11 -0800 Subject: [PATCH 028/317] Adjust mockery... --- letsencrypt/cli.py | 5 ++--- letsencrypt/main.py | 8 ++++---- letsencrypt/tests/cli_test.py | 10 +++++----- letsencrypt/tests/display/ops_test.py | 24 ++++++++++++------------ 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index cb6f66be9..1922cf73b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -22,8 +22,7 @@ from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt.plugins import disco as plugins_disco -from letsencrypt.plugins.selection import cli_plugin_requests - +import letsencrypt.plugins.selection as plugin_selection logger = logging.getLogger(__name__) @@ -118,7 +117,7 @@ def set_by_cli(var): detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator - auth, inst = cli_plugin_requests(detector) + auth, inst = plugin_selection.cli_plugin_requests(detector) detector.authenticator = auth if auth else "" detector.installer = inst if inst else "" logger.debug("Default Detector is %r", detector) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 153e118a4..d9e1456ad 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -25,7 +25,7 @@ from letsencrypt import storage from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco -from letsencrypt.plugins.selection import choose_configurator_plugins +from letsencrypt.plugins import selection as ps import traceback import logging.handlers @@ -405,7 +405,7 @@ def install(config, plugins): # this function ... try: - installer, _ = choose_configurator_plugins(config, plugins, "install") + installer, _ = ps.choose_configurator_plugins(config, plugins, "install") except errors.PluginSelectionError as e: return e.message @@ -480,7 +480,7 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals # TODO: Make run as close to auth + install as possible # Possible difficulties: config.csr was hacked into auth try: - installer, authenticator = choose_configurator_plugins(config, plugins, "run") + installer, authenticator = ps.choose_configurator_plugins(config, plugins, "run") except errors.PluginSelectionError as e: return e.message @@ -510,7 +510,7 @@ def obtain_cert(config, plugins, lineage=None): # pylint: disable=too-many-locals try: # installers are used in auth mode to determine domain names - installer, authenticator = choose_configurator_plugins(config, plugins, "certonly") + installer, authenticator = ps.choose_configurator_plugins(config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index f1f539016..918addcf8 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -201,12 +201,12 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(args.chain_path, os.path.abspath(chain)) self.assertEqual(args.fullchain_path, os.path.abspath(fullchain)) - @mock.patch('letsencrypt.main.cli.record_chosen_plugins') - @mock.patch('letsencrypt.main.cli.display_ops') - def test_installer_selection(self, mock_display_ops, _rec): + @mock.patch('letsencrypt.main.ps.record_chosen_plugins') + @mock.patch('letsencrypt.main.ps.pick_installer') + def test_installer_selection(self, mock_pick_installer, _rec): self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert', '--key-path', 'key', '--chain-path', 'chain']) - self.assertEqual(mock_display_ops.pick_installer.call_count, 1) + self.assertEqual(mock_pick_installer.call_count, 1) @mock.patch('letsencrypt.le_util.exe_exists') def test_configurator_selection(self, mock_exe_exists): @@ -493,7 +493,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._webroot_map_test(simple_map, "/tmp2", "eg2.com,eg.com", expected_map, domains) # test inclusion of interactively specified domains in the webroot map - with mock.patch('letsencrypt.cli.display_ops.choose_names') as mock_choose: + with mock.patch('letsencrypt.display.ops.choose_names') as mock_choose: mock_choose.return_value = domains expected_map["eg2.com"] = "/tmp" self._webroot_map_test(None, "/tmp", None, expected_map, domains) diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index 8a52c872b..0dacdfea8 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -101,17 +101,17 @@ class ChooseAccountTest(unittest.TestCase): from letsencrypt.display import ops return ops.choose_account(accounts) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_one(self, mock_util): mock_util().menu.return_value = (display_util.OK, 0) self.assertEqual(self._call([self.acc1]), self.acc1) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_two(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertEqual(self._call([self.acc1, self.acc2]), self.acc2) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_cancel(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 1) self.assertTrue(self._call([self.acc1, self.acc2]) is None) @@ -199,12 +199,12 @@ class ChooseNamesTest(unittest.TestCase): self._call(None) self.assertEqual(mock_manual.call_count, 1) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_no_installer_cancel(self, mock_util): mock_util().input.return_value = (display_util.CANCEL, []) self.assertEqual(self._call(None), []) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_no_names_choose(self, mock_util): self.mock_install().get_all_names.return_value = set() mock_util().yesno.return_value = True @@ -215,14 +215,14 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(mock_util().input.call_count, 1) self.assertEqual(actual_doms, [domain]) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_no_names_quit(self, mock_util): self.mock_install().get_all_names.return_value = set() mock_util().yesno.return_value = False self.assertEqual(self._call(self.mock_install), []) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_filter_names_valid_return(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = (display_util.OK, ["example.com"]) @@ -231,14 +231,14 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(names, ["example.com"]) self.assertEqual(mock_util().checklist.call_count, 1) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_filter_names_nothing_selected(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = (display_util.OK, []) self.assertEqual(self._call(self.mock_install), []) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_filter_names_cancel(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = ( @@ -257,7 +257,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(get_valid_domains(all_invalid), []) self.assertEqual(len(get_valid_domains(two_valid)), 2) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_choose_manually(self, mock_util): from letsencrypt.display.ops import _choose_names_manually # No retry @@ -305,7 +305,7 @@ class SuccessInstallationTest(unittest.TestCase): from letsencrypt.display.ops import success_installation success_installation(names) - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_success_installation(self, mock_util): mock_util().notification.return_value = None names = ["example.com", "abc.com"] @@ -327,7 +327,7 @@ class SuccessRenewalTest(unittest.TestCase): from letsencrypt.display.ops import success_renewal success_renewal(names, "renew") - @mock.patch("letsencrypt.display.ops.util") + @mock.patch("letsencrypt.display.ops.z_util") def test_success_renewal(self, mock_util): mock_util().notification.return_value = None names = ["example.com", "abc.com"] From fcf1ea32d89c942dc65fb8e877c9d577dd63ecb7 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 16:01:52 -0800 Subject: [PATCH 029/317] fixup --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 1922cf73b..f0ea7153b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -22,7 +22,7 @@ from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt.plugins import disco as plugins_disco -import letsencrypt.plugins.selection as plugin_selection +import letsencrypt.plugins.selection as plugin_selection logger = logging.getLogger(__name__) From 0aed0e90f1a1a8e0ca67b83e7187ac44966b593e Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Fri, 11 Mar 2016 17:07:13 -0800 Subject: [PATCH 030/317] don't include files that have multiple vhosts --- letsencrypt-apache/letsencrypt_apache/configurator.py | 1 + letsencrypt-apache/letsencrypt_apache/display_ops.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 07c145bfc..3a679fa7e 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -545,6 +545,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): paths = self.aug.match( ("/files%s//*[label()=~regexp('%s')]" % (vhost_path, parser.case_i("VirtualHost")))) + paths = [path for path in paths if os.path.basename(path) == "VirtualHost"] for path in paths: new_vhost = self._create_vhost(path) realpath = os.path.realpath(new_vhost.filep) diff --git a/letsencrypt-apache/letsencrypt_apache/display_ops.py b/letsencrypt-apache/letsencrypt_apache/display_ops.py index 6a2308d73..bd3aa524d 100644 --- a/letsencrypt-apache/letsencrypt_apache/display_ops.py +++ b/letsencrypt-apache/letsencrypt_apache/display_ops.py @@ -83,7 +83,8 @@ def _vhost_menu(domain, vhosts): code, tag = zope.component.getUtility(interfaces.IDisplay).menu( "We were unable to find a vhost with a ServerName " "or Address of {0}.{1}Which virtual host would you " - "like to choose?".format(domain, os.linesep), + "like to choose?\n(note: conf files with multiple " + "vhosts are not currently supported)".format(domain, os.linesep), choices, help_label="More Info", ok_label="Select") except errors.MissingCommandlineFlag as e: msg = ("Failed to run Apache plugin non-interactively{1}{0}{1}" From aac692f8ca812e62ba599ddddfe67eff23786e83 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 17:27:10 -0800 Subject: [PATCH 031/317] Mock-picking fix --- letsencrypt/tests/client_test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 8be2178b9..4331be59d 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -422,9 +422,8 @@ class RollbackTest(unittest.TestCase): @classmethod def _call(cls, checkpoints, side_effect): from letsencrypt.client import rollback - with mock.patch("letsencrypt.client" - ".display_ops.pick_installer") as mock_pick_installer: - mock_pick_installer.side_effect = side_effect + with mock.patch("letsencrypt.client.plugin_selection.pick_installer") as mpi + mpi.side_effect = side_effect rollback(None, checkpoints, {}, mock.MagicMock()) def test_no_problems(self): From b888a2bd526d758d5c5f2d23c16870db1212b186 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 11 Mar 2016 17:30:02 -0800 Subject: [PATCH 032/317] Fixup --- letsencrypt/tests/client_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 4331be59d..f14a52407 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -422,7 +422,7 @@ class RollbackTest(unittest.TestCase): @classmethod def _call(cls, checkpoints, side_effect): from letsencrypt.client import rollback - with mock.patch("letsencrypt.client.plugin_selection.pick_installer") as mpi + with mock.patch("letsencrypt.client.plugin_selection.pick_installer") as mpi: mpi.side_effect = side_effect rollback(None, checkpoints, {}, mock.MagicMock()) From aefab5cc3267df08d3b0949c0de8436dc2af9aad Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Mon, 14 Mar 2016 15:11:52 +0200 Subject: [PATCH 033/317] Fixing tests --- letsencrypt/tests/auth_handler_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 6398ec7a4..24718fa82 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -126,7 +126,10 @@ class GetAuthorizationsTest(unittest.TestCase): for achall in self.mock_auth.cleanup.call_args[0][0]: self.assertTrue(achall.typ in ["tls-sni-01", "http-01", "dns"]) - self.assertEqual(len(authzr), 1) + # Length of authorizations list + self.assertEqual(len(authzr[0]), 1) + # Length of valid domains list + self.assertEqual(len(authzr[1]), 1) @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") def test_name3_tls_sni_01_3(self, mock_poll): From 4be308ac0e669365441c153f7e4f35b24ea64570 Mon Sep 17 00:00:00 2001 From: Robert Xiao Date: Thu, 11 Feb 2016 16:56:53 -0500 Subject: [PATCH 034/317] Support MacPorts on OS X. This fixes #2447. Notably, this also installs pip via the recommended `get-pip` route rather than grabbing a whole new version over Homebrew; this allows the install to work with OS X's built-in Python or with the python.org Python. --- letsencrypt-auto-source/letsencrypt-auto | 43 +++++++++++++------ .../pieces/bootstrappers/mac.sh | 43 +++++++++++++------ 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 0590e5d43..79ce3d3cb 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -348,28 +348,45 @@ BootstrapFreeBsd() { } BootstrapMac() { - if ! hash brew 2>/dev/null; then - echo "Homebrew not installed.\nDownloading..." - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" fi - if [ -z "$(brew list --versions augeas)" ]; then - echo "augeas not installed.\nInstalling augeas from Homebrew..." - brew install augeas + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python fi - if [ -z "$(brew list --versions dialog)" ]; then - echo "dialog not installed.\nInstalling dialog from Homebrew..." - brew install dialog + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib fi - if [ -z "$(brew list --versions python)" ]; then - echo "python not installed.\nInstalling python from Homebrew..." - brew install python + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv not installed.\nInstalling with pip..." + echo "virtualenv not installed." + echo "Installing with pip..." pip install virtualenv fi } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index 4bdf34116..79e58eb3f 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -1,26 +1,43 @@ BootstrapMac() { - if ! hash brew 2>/dev/null; then - echo "Homebrew not installed.\nDownloading..." - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" fi - if [ -z "$(brew list --versions augeas)" ]; then - echo "augeas not installed.\nInstalling augeas from Homebrew..." - brew install augeas + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python fi - if [ -z "$(brew list --versions dialog)" ]; then - echo "dialog not installed.\nInstalling dialog from Homebrew..." - brew install dialog + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib fi - if [ -z "$(brew list --versions python)" ]; then - echo "python not installed.\nInstalling python from Homebrew..." - brew install python + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv not installed.\nInstalling with pip..." + echo "virtualenv not installed." + echo "Installing with pip..." pip install virtualenv fi } From 2054150abb430e125b89cde7eaa03a8b34896932 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Mon, 14 Mar 2016 15:35:20 -0700 Subject: [PATCH 035/317] Work in progress --- letsencrypt/cli.py | 14 ++++++++ letsencrypt/storage.py | 77 +++++++++++++++++++++++++++++++++--------- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 024f53d0b..0992f0877 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -537,6 +537,20 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): raise errors.PluginSelectionError(msg) +def _relevant(option): + """ + Is this option one that could be restored and used for future renewal purposes? + :param str option: the name of the option + + :rtype: bool + """ + # The list() here produces a list of the plugin names as strings. + plugins = list(plugins_disco.PluginsRegistry.find_all()) + return (option in STR_CONFIG_ITEMS + or option in INT_CONFIG_ITEMS + or any(option.startswith(x + "_") for x in plugins)) + + def set_configurator(previously, now): """ Setting configurators multiple ways is okay, as long as they all agree diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index cff2d53e1..bea686e19 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -50,13 +50,12 @@ def add_time_interval(base_time, interval, textparser=parsedatetime.Calendar()): return textparser.parseDT(interval, base_time, tzinfo=tzinfo)[0] -def write_renewal_config(filename, target, cli_config): +def write_renewal_config(filename, target, relevant_data): """Writes a renewal config file with the specified name and values. :param str filename: Absolute path to the config file :param dict target: Maps ALL_FOUR to their symlink paths - :param .RenewerConfiguration cli_config: parsed command line - arguments + :param dict relevant_data: Renewal configuration options to save :returns: Configuration object for the new config file :rtype: configobj.ConfigObj @@ -67,18 +66,11 @@ def write_renewal_config(filename, target, cli_config): for kind in ALL_FOUR: config[kind] = target[kind] - # XXX: We clearly need a more general and correct way of getting - # options into the configobj for the RenewableCert instance. - # This is a quick-and-dirty way to do it to allow integration - # testing to start. (Note that the config parameter to new_lineage - # ideally should be a ConfigObj, but in this case a dict will be - # accepted in practice.) - renewalparams = vars(cli_config.namespace) - if renewalparams: - config["renewalparams"] = renewalparams + if relevant_data: + config["renewalparams"] = relevant_data config.comments["renewalparams"] = ["", - "Options and defaults used" - " in the renewal process"] + "Options used in " + "the renewal process"] # TODO: add human-readable comments explaining other available # parameters @@ -106,7 +98,10 @@ def update_configuration(lineagename, target, cli_config): # If an existing tempfile exists, delete it if os.path.exists(temp_filename): os.unlink(temp_filename) - write_renewal_config(temp_filename, target, cli_config) + + # Save only the config items that are relevant to renewal + values = relevant_values(vars(cli_config.namespace)) + write_renewal_config(temp_filename, target, values) os.rename(temp_filename, config_filename) return configobj.ConfigObj(config_filename) @@ -127,6 +122,50 @@ def get_link_target(link): return os.path.abspath(target) +def relevant_values(all_values): + """Return a new dict containing only items relevant for renewal. + + :param dict all_values: The original values. + + :returns: A new dictionary containing items that can be used in renewal. + :rtype dict:""" + + from letsencrypt import cli + + import code + code.interact(local=locals()) + def _is_cli_default(option, value): + # Look through the CLI parser defaults and see if this option is + # both present and equal to the specified value. If not, return + # False. + for x in cli._parser.parser._actions: + if x.dest == option: + if x.default == value: + return True + else: + break + return False + + values = dict() + for option, value in all_values.iteritems(): + # Try to find reasons to store this item in the + # renewal config. It can be stored if it is relevant and + # (it is _set_by_cli() or flag_default() is different + # from the value or flag_default() doesn't exist). + if cli._relevant(option): + if (cli._set_by_cli(option) + or not _is_cli_default(option, value)): +# or option not in constants.CLI_DEFAULTS +# or constants.CLI_DEFAULTS[option] != value): + values[option] = value + print option, value + else: + print "not saving", option, value + else: + print "not relevant", option, value + return values + + class RenewableCert(object): # pylint: disable=too-many-instance-attributes """Renewable certificate. @@ -690,6 +729,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes :rtype: :class:`storage.renewableCert` """ + # Examine the configuration and find the new lineage's name for i in (cli_config.renewal_configs_dir, cli_config.archive_dir, cli_config.live_dir): @@ -744,7 +784,12 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # Document what we've done in a new renewal config file config_file.close() - new_config = write_renewal_config(config_filename, target, cli_config) + + # Save only the config items that are relevant to renewal + values = relevant_values(vars(cli_config.namespace)) + print (values) + + new_config = write_renewal_config(config_filename, target, values) return cls(new_config.filename, cli_config) def save_successor(self, prior_version, new_cert, From bd1b5229406991a03dcccea01f483607bb26fd10 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Mon, 14 Mar 2016 17:11:09 -0700 Subject: [PATCH 036/317] Remove interact() --- letsencrypt/storage.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index bea686e19..672d3ff1f 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -132,8 +132,6 @@ def relevant_values(all_values): from letsencrypt import cli - import code - code.interact(local=locals()) def _is_cli_default(option, value): # Look through the CLI parser defaults and see if this option is # both present and equal to the specified value. If not, return From b31372b2714f00db21cdfb44369b27b64ab4ea4f Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Mon, 14 Mar 2016 17:13:52 -0700 Subject: [PATCH 037/317] Remove debug prints --- letsencrypt/storage.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 672d3ff1f..263803d7f 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -157,10 +157,6 @@ def relevant_values(all_values): # or constants.CLI_DEFAULTS[option] != value): values[option] = value print option, value - else: - print "not saving", option, value - else: - print "not relevant", option, value return values From 62679b2b21c4ee49a4be055e68828a00aa1ed115 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Mon, 14 Mar 2016 17:28:35 -0700 Subject: [PATCH 038/317] Test coverage for storage.py changes --- letsencrypt/tests/storage_test.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 7bc31dab3..0d89156d3 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -557,6 +557,22 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10))) self.assertFalse(os.path.exists(temp_config_file)) + def test_relevant_values(self): + """Test that relevant_values() can reject an irrelevant value.""" + from letsencrypt import storage + self.assertEqual(storage.relevant_values({"hello": "there"}), {}) + + def test_relevant_values_default(self): + """Test that relevant_values() can reject a default value.""" + from letsencrypt import storage + self.assertEqual(storage.relevant_values({"rsa_key_size": 2048}), {}) + + def test_relevant_values_nondefault(self): + """Test that relevant_values() can retain a non-default value.""" + from letsencrypt import storage + self.assertEqual(storage.relevant_values({"rsa_key_size": 12}), + {"rsa_key_size": 12}) + def test_new_lineage(self): """Test for new_lineage() class method.""" from letsencrypt import storage From 29a4b2a37e557c97e864e2c38abce0de72966978 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Mon, 14 Mar 2016 17:40:26 -0700 Subject: [PATCH 039/317] Remove two more debug prints --- letsencrypt/storage.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 263803d7f..5f5064f7d 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -156,7 +156,6 @@ def relevant_values(all_values): # or option not in constants.CLI_DEFAULTS # or constants.CLI_DEFAULTS[option] != value): values[option] = value - print option, value return values @@ -781,7 +780,6 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) - print (values) new_config = write_renewal_config(config_filename, target, values) return cls(new_config.filename, cli_config) From b19c74be327c03fdb2810fc492fbbc8a6e7ec71b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 14 Mar 2016 18:44:29 -0700 Subject: [PATCH 040/317] Address review comments --- letsencrypt/cli.py | 7 +++---- letsencrypt/main.py | 11 ++++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b76311777..c4d127718 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1,4 +1,4 @@ -"""Let's Encrypt command CLI argument processing.""" +"""Let's Encrypt command line argument & config processing.""" # pylint: disable=too-many-lines from __future__ import print_function import argparse @@ -634,10 +634,9 @@ class HelpfulArgumentParser(object): from letsencrypt import main self.VERBS = main.VERBS # List of topics for which additional help can be provided - HELP_TOPICS = ["all", "security", - "paths", "automation", "testing"] + list(six.iterkeys(self.VERBS)) + HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + list(self.VERBS) - plugin_names = list(six.iterkeys(plugins)) + plugin_names = list(plugins) self.help_topics = HELP_TOPICS + plugin_names + [None] usage, short_usage = usage_strings(plugins) self.parser = configargparse.ArgParser( diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 56725d300..d2d2c55ac 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -2,8 +2,13 @@ from __future__ import print_function import atexit import functools +import logging.handlers import os import sys +import time +import traceback + +import OpenSSL import zope.component import letsencrypt @@ -22,15 +27,11 @@ from letsencrypt import log from letsencrypt import reporter from letsencrypt import storage +from acme import jose from letsencrypt.cli import choose_configurator_plugins, _renewal_conf_files, should_renew from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco -import traceback -import logging.handlers -import time -from acme import jose -import OpenSSL logger = logging.getLogger(__name__) From 55eeb655bbcc55520b5a415d68e812d7d9166a5a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 14 Mar 2016 20:11:31 -0700 Subject: [PATCH 041/317] Moved VERBS back to cli.py --- letsencrypt/cli.py | 8 +++++-- letsencrypt/main.py | 9 -------- letsencrypt/tests/cli_test.py | 39 +++++------------------------------ 3 files changed, 11 insertions(+), 45 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 85a0d1d8a..662e1a94b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -629,9 +629,13 @@ class HelpfulArgumentParser(object): """ def __init__(self, args, plugins, detect_defaults=False): - from letsencrypt import main - self.VERBS = main.VERBS + self.VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, + "config_changes": main.config_changes, "run": main.run, + "install": main.install, "plugins": main.plugins_cmd, + "renew": renew, "revoke": main.revoke, + "rollback": main.rollback, "everything": main.run} + # List of topics for which additional help can be provided HELP_TOPICS = ["all", "security", "paths", "automation", "testing"] + list(self.VERBS) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 19636b93e..264f7625e 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -701,15 +701,6 @@ def main(cli_args=sys.argv[1:]): return config.func(config, plugins) -# Maps verbs/subcommands to the functions that implement them -# In principle this should live in cli.HelpfulArgumentParser, but -# due to issues with import cycles and testing, it lives here -VERBS = {"auth": obtain_cert, "certonly": obtain_cert, - "config_changes": config_changes, "everything": run, - "install": install, "plugins": plugins_cmd, "renew": cli.renew, - "revoke": revoke, "rollback": rollback, "run": run} - - if __name__ == "__main__": err_string = main() if err_string: diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index c5865206d..7b901d410 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -79,7 +79,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods return ret, None, stderr, client def test_no_flags(self): - with MockedVerb("run") as mock_run: + with mock.patch('letsencrypt.main.run') as mock_run: self._call([]) self.assertEqual(1, mock_run.call_count) @@ -190,7 +190,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods chain = 'chain' fullchain = 'fullchain' - with MockedVerb('install') as mock_install: + with mock.patch('letsencrypt.main.install') as mock_install: self._call(['install', '--cert-path', cert, '--key-path', 'key', '--chain-path', 'chain', '--fullchain-path', 'fullchain']) @@ -248,7 +248,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods unused_config, auth, unused_installer = mock_init.call_args[0] self.assertTrue(isinstance(auth, manual.Authenticator)) - with MockedVerb("certonly") as mock_certonly: + with mock.patch('letsencrypt.main.obtain_cert') as mock_certonly: self._call(["auth", "--standalone"]) self.assertEqual(1, mock_certonly.call_count) @@ -321,7 +321,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods chain = 'chain' fullchain = 'fullchain' - with MockedVerb('certonly') as mock_obtaincert: + with mock.patch('letsencrypt.main.obtain_cert') as mock_obtaincert: self._call(['certonly', '--cert-path', cert, '--key-path', 'key', '--chain-path', 'chain', '--fullchain-path', 'fullchain']) @@ -900,7 +900,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(contents, test_contents) def test_agree_dev_preview_config(self): - with MockedVerb('run') as mocked_run: + with mock.patch('letsencrypt.main.run') as mocked_run: self._call(['-c', test_util.vector_path('cli.ini')]) self.assertTrue(mocked_run.called) @@ -1010,34 +1010,5 @@ class DuplicativeCertsTest(storage_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 - 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): - self.verb_dict = main.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 1f22b3cbefab8e9100e6d2e6a5f502ea1f4cf6ae Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Mon, 14 Mar 2016 20:58:20 -0700 Subject: [PATCH 042/317] Move _relevant from cli.py to storage.py --- letsencrypt/cli.py | 14 -------------- letsencrypt/storage.py | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 12ed74edc..8e545a5de 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -537,20 +537,6 @@ def diagnose_configurator_problem(cfg_type, requested, plugins): raise errors.PluginSelectionError(msg) -def _relevant(option): - """ - Is this option one that could be restored and used for future renewal purposes? - :param str option: the name of the option - - :rtype: bool - """ - # The list() here produces a list of the plugin names as strings. - plugins = list(plugins_disco.PluginsRegistry.find_all()) - return (option in STR_CONFIG_ITEMS - or option in INT_CONFIG_ITEMS - or any(option.startswith(x + "_") for x in plugins)) - - def set_configurator(previously, now): """ Setting configurators multiple ways is okay, as long as they all agree diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 5f5064f7d..5e5d70518 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -122,6 +122,22 @@ def get_link_target(link): return os.path.abspath(target) +def _relevant(option): + """ + Is this option one that could be restored for future renewal purposes? + :param str option: the name of the option + + :rtype: bool + """ + # The list() here produces a list of the plugin names as strings. + from letsencrypt import cli + from letsencrypt.plugins import disco as plugins_disco + plugins = list(plugins_disco.PluginsRegistry.find_all()) + return (option in cli.STR_CONFIG_ITEMS + or option in cli.INT_CONFIG_ITEMS + or any(option.startswith(x + "_") for x in plugins)) + + def relevant_values(all_values): """Return a new dict containing only items relevant for renewal. @@ -150,7 +166,7 @@ def relevant_values(all_values): # renewal config. It can be stored if it is relevant and # (it is _set_by_cli() or flag_default() is different # from the value or flag_default() doesn't exist). - if cli._relevant(option): + if _relevant(option): if (cli._set_by_cli(option) or not _is_cli_default(option, value)): # or option not in constants.CLI_DEFAULTS From 68ca289e7f84b7579787751a22bc56216b4ec955 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 15 Mar 2016 12:07:46 -0700 Subject: [PATCH 043/317] change wording --- letsencrypt-apache/letsencrypt_apache/display_ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/display_ops.py b/letsencrypt-apache/letsencrypt_apache/display_ops.py index bd3aa524d..4c01579cc 100644 --- a/letsencrypt-apache/letsencrypt_apache/display_ops.py +++ b/letsencrypt-apache/letsencrypt_apache/display_ops.py @@ -84,7 +84,7 @@ def _vhost_menu(domain, vhosts): "We were unable to find a vhost with a ServerName " "or Address of {0}.{1}Which virtual host would you " "like to choose?\n(note: conf files with multiple " - "vhosts are not currently supported)".format(domain, os.linesep), + "vhosts are not yet supported)".format(domain, os.linesep), choices, help_label="More Info", ok_label="Select") except errors.MissingCommandlineFlag as e: msg = ("Failed to run Apache plugin non-interactively{1}{0}{1}" From 97dba025cfa4d0e275e3af1b2c93cea4fd72ba3d Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Wed, 16 Mar 2016 00:37:39 +0200 Subject: [PATCH 044/317] Logging failed domains --- letsencrypt/auth_handler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index d63b29156..3a3dc7c58 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -78,6 +78,10 @@ class AuthHandler(object): if response: failed_domains = failed_domains.union(response) + for domain in failed_domains: + logger.warning( + "Challenge failed for domain %s", + domain) returnDomains = [domain for domain in domains if domain not in failed_domains] From f254d6def137eca5ca4a53fb4b45e48380aea39f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 15 Mar 2016 17:27:19 -0700 Subject: [PATCH 045/317] fix spacing --- 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 bfd3818c1..3cc65fd11 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -133,7 +133,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods out = self._help_output(['-h']) self.assertTrue(cli.usage_strings(plugins)[0] in out) - def _cli_missing_flag(self, args, message): "Ensure that a particular error raises a missing cli flag error containing message" exc = None From 9444c9e54b8bcdd65d81245e0b382f40acb71616 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 15 Mar 2016 17:34:15 -0700 Subject: [PATCH 046/317] Make _test_renew_common more useful --- letsencrypt/tests/cli_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 3cc65fd11..9b474522e 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -679,8 +679,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with open(os.path.join(renewer_configs_dir, 'test.conf'), 'w') as f: f.write("My contents don't matter") - def _test_renew_common(self, renewalparams=None, error_expected=False, - names=None, assert_oc_called=None): + def _test_renew_common(self, renewalparams=None, names=None, + assert_oc_called=None, **kwargs): self._make_dummy_renewal_config() with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() @@ -691,8 +691,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_lineage.names.return_value = names mock_rc.return_value = mock_lineage with mock.patch('letsencrypt.cli.obtain_cert') as mock_obtain_cert: - self._test_renewal_common(True, None, error_expected=error_expected, - args=['renew'], renew=False) + kwargs.setdefault('args', ['renew']) + self._test_renewal_common(True, None, renew=False, **kwargs) if assert_oc_called is not None: if assert_oc_called: self.assertTrue(mock_obtain_cert.called) From b3fbb05f487a5e15ed7cb5ff9737bf7b7cd7ae4c Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 15 Mar 2016 17:42:37 -0700 Subject: [PATCH 047/317] More test cleanup --- 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 9b474522e..66edb3be2 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -715,7 +715,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_renew_with_nonetype_http01(self): renewalparams = {'authenticator': 'webroot', 'http01_port': 'None'} - self._test_renew_common(renewalparams=renewalparams, error_expected=False, + self._test_renew_common(renewalparams=renewalparams, assert_oc_called=True) def test_renew_with_bad_domain(self): From 640582d4b62c0288f287f5218fae407f8d361cca Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 15 Mar 2016 18:29:12 -0700 Subject: [PATCH 048/317] fixes #2668 --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 8e545a5de..e0aea0916 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -543,7 +543,7 @@ def set_configurator(previously, now): :param str previously: previously identified request for the installer/authenticator :param str requested: the request currently being processed """ - if now is None: + if not now: # we're not actually setting anything return previously if previously: From 910a437f5e2f7d7ecf54f2368f8824c6096eecbe Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 15 Mar 2016 18:35:34 -0700 Subject: [PATCH 049/317] Add test to prevent regressions --- letsencrypt/tests/cli_test.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 66edb3be2..e4a9dc3de 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -51,6 +51,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def tearDown(self): shutil.rmtree(self.tmp_dir) + # Reset globals in cli + # pylint: disable=protected-access + cli._parser = cli._set_by_cli.detector = None def _call(self, args): "Run the cli with output streams and actual client mocked out" @@ -724,6 +727,12 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._test_renew_common(renewalparams=renewalparams, error_expected=True, names=names, assert_oc_called=False) + def test_renew_with_configurator(self): + renewalparams = {'authenticator': 'webroot'} + self._test_renew_common( + renewalparams=renewalparams, assert_oc_called=True, + args='renew --configurator apache'.split()) + def test_renew_plugin_config_restoration(self): renewalparams = {'authenticator': 'webroot', 'webroot_path': 'None', From 1390c96e65259c216dec172159696d0e80f10d24 Mon Sep 17 00:00:00 2001 From: Benjamin Neff Date: Wed, 16 Mar 2016 14:38:50 +0100 Subject: [PATCH 050/317] handle permission denied during cleanup if the .well-known/acme-challenge was created before and the user has no permissions to delete it, it failed at cleanup. --- letsencrypt/plugins/webroot.py | 3 +++ letsencrypt/plugins/webroot_test.py | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 0e3ebe1a7..6d2899511 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -152,5 +152,8 @@ to serve all files under specified web root ({0}).""" if exc.errno == errno.ENOTEMPTY: logger.debug("Challenges cleaned up but %s not empty", root_path) + elif exc.errno == errno.EACCES: + logger.debug("Challenges cleaned up but no permissions for %s", + root_path) else: raise diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 8c1427340..ed0326555 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -158,7 +158,7 @@ class AuthenticatorTest(unittest.TestCase): os.rmdir(leftover_path) @mock.patch('os.rmdir') - def test_cleanup_oserror(self, mock_rmdir): + def test_cleanup_permission_denied(self, mock_rmdir): self.auth.prepare() self.auth.perform([self.achall]) @@ -166,10 +166,22 @@ class AuthenticatorTest(unittest.TestCase): os_error.errno = errno.EACCES mock_rmdir.side_effect = os_error + self.auth.cleanup([self.achall]) + self.assertFalse(os.path.exists(self.validation_path)) + self.assertTrue(os.path.exists(self.root_challenge_path)) + + @mock.patch('os.rmdir') + def test_cleanup_oserror(self, mock_rmdir): + self.auth.prepare() + self.auth.perform([self.achall]) + + os_error = OSError() + os_error.errno = errno.ENOENT + mock_rmdir.side_effect = os_error + self.assertRaises(OSError, self.auth.cleanup, [self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) - if __name__ == "__main__": unittest.main() # pragma: no cover From 4d6a1ee7ff23b29d4416ee91610a7d8a58173292 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Thu, 17 Mar 2016 08:30:07 +0200 Subject: [PATCH 051/317] Cleaning up code based on bmw's comments --- letsencrypt/auth_handler.py | 40 +++++++++++--------------- letsencrypt/cli.py | 11 +++---- letsencrypt/client.py | 21 ++++++++------ letsencrypt/tests/auth_handler_test.py | 8 ++---- letsencrypt/tests/client_test.py | 16 +++++++++-- 5 files changed, 52 insertions(+), 44 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 3a3dc7c58..1d82705b7 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -66,31 +66,27 @@ class AuthHandler(object): self._choose_challenges(domains) - failed_domains = set() # While there are still challenges remaining... while self.achalls: resp = self._solve_challenges() logger.info("Waiting for verification...") - # Send all Responses - this modifies dv_c and cont_c - response = self._respond(resp, best_effort) - - if response: - failed_domains = failed_domains.union(response) - for domain in failed_domains: - logger.warning( - "Challenge failed for domain %s", - domain) - - returnDomains = [domain for domain in domains - if domain not in failed_domains] + # Send all Responses - this modifies achalls + self._respond(resp, best_effort) # Just make sure all decisions are complete. self.verify_authzr_complete() + # Only return valid authorizations - return [authzr for authzr in self.authzr.values() - if authzr.body.status == messages.STATUS_VALID], returnDomains + retVal = [authzr for authzr in self.authzr.values() + if authzr.body.status == messages.STATUS_VALID] + + if len(retVal) <= 0: + logger.critical("Challenges failed for all domains") + raise + + return retVal def _choose_challenges(self, domains): """Retrieve necessary challenges to satisfy server.""" @@ -134,13 +130,11 @@ class AuthHandler(object): # Check for updated status... try: - failed_domains = self._poll_challenges(chall_update, best_effort) + self._poll_challenges(chall_update, best_effort) finally: # This removes challenges from self.achalls self._cleanup_challenges(active_achalls) - return failed_domains - def _send_responses(self, achalls, resps, chall_update): """Send responses and make sure errors are handled. @@ -172,7 +166,6 @@ class AuthHandler(object): """Wait for all challenge results to be determined.""" dom_to_check = set(chall_update.keys()) comp_domains = set() - failed_domains = set() rounds = 0 while dom_to_check and rounds < max_rounds: @@ -191,7 +184,10 @@ class AuthHandler(object): # We failed some challenges... damage control else: if best_effort: - failed_domains.add(domain) + comp_domains.add(domain) + logger.warning( + "Challenge failed for domain %s", + domain) else: all_failed_achalls.update( updated for _, updated in failed_achalls) @@ -200,12 +196,10 @@ class AuthHandler(object): _report_failed_challs(all_failed_achalls) raise errors.FailedChallenges(all_failed_achalls) - dom_to_check -= comp_domains.union(failed_domains) + dom_to_check -= comp_domains comp_domains.clear() rounds += 1 - return failed_domains - def _handle_check(self, domain, achalls): """Returns tuple of ('completed', 'failed').""" completed = [] diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 4a0c80725..2ccff232b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -694,7 +694,7 @@ def obtain_cert(config, plugins, lineage=None): if config.csr is not None: assert lineage is None, "Did not expect a CSR with a RenewableCert" csr, typ = config.actual_csr - certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ, authzr=False) + certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) if config.dry_run: logger.info( "Dry run: skipping saving certificate to %s", config.cert_path) @@ -1621,10 +1621,11 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): help="Require that all configuration files are owned by the current " "user; only needed if your config is somewhere unsafe like /tmp/") helpful.add( - "automation", "--allow-subset-of-names", dest="allow_subset_of_names", - action="store_true", default=False, - help="Allow subsets of domain names in a single lineage to fail " - "validation without exiting.") + "automation", "--allow-subset-of-names", + action="store_true", + help="When performing domain validation, do not consider it a failure " + "if authorizations can not be obtained for a strict subset of " + "the requested domains. This option cannot be used with --csr.") helpful.add_group( "renew", description="The 'renew' subcommand will attempt to renew all" diff --git a/letsencrypt/client.py b/letsencrypt/client.py index f1a36197e..f2c3d1636 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -189,7 +189,7 @@ class Client(object): self.auth_handler = None def obtain_certificate_from_csr(self, domains, csr, - typ=OpenSSL.crypto.FILETYPE_ASN1, authzr=False): + typ=OpenSSL.crypto.FILETYPE_ASN1, authzr=None): """Obtain certificate. Internal function with precondition that `domains` are @@ -199,6 +199,8 @@ class Client(object): :param .le_util.CSR csr: DER-encoded Certificate Signing Request. The key used to generate this CSR can be different than `authkey`. + :param dict authzr: ACME Authorization Resource dict where keys are + domains and values are :class:`acme.messages.AuthorizationResource` :returns: `.CertificateResource` and certificate chain (as returned by `.fetch_chain`). @@ -215,10 +217,8 @@ class Client(object): logger.debug("CSR: %s, domains: %s", csr, domains) - if authzr is False: - authzr, _ = self.auth_handler.get_authorizations( - domains, - self.config.allow_subset_of_names) + if authzr is None: + authzr = self.auth_handler.get_authorizations(domains) certr = self.acme.request_issuance( jose.ComparableX509( @@ -240,15 +240,20 @@ class Client(object): :rtype: tuple """ - authzr, domains = self.auth_handler.get_authorizations(domains, - self.config.allow_subset_of_names) + authzr = self.auth_handler.get_authorizations( + domains, + self.config.allow_subset_of_names) + + domains = [a.body.identifier.value.encode('ascii', 'ignore') + for a in authzr] # Create CSR from names key = crypto_util.init_save_key( self.config.rsa_key_size, self.config.key_dir) csr = crypto_util.init_save_csr(key, domains, self.config.csr_dir) - return self.obtain_certificate_from_csr(domains, csr, authzr=authzr) + (key, csr) + return (self.obtain_certificate_from_csr(domains, csr, authzr=authzr) + + (key, csr)) def obtain_and_enroll_certificate(self, domains): """Obtain and enroll certificate. diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 24718fa82..61dd0e21b 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -87,7 +87,7 @@ class GetAuthorizationsTest(unittest.TestCase): mock_poll.side_effect = self._validate_all - authzr, _ = self.handler.get_authorizations(["0"]) + authzr = self.handler.get_authorizations(["0"]) self.assertEqual(self.mock_net.answer_challenge.call_count, 1) @@ -127,9 +127,7 @@ class GetAuthorizationsTest(unittest.TestCase): self.assertTrue(achall.typ in ["tls-sni-01", "http-01", "dns"]) # Length of authorizations list - self.assertEqual(len(authzr[0]), 1) - # Length of valid domains list - self.assertEqual(len(authzr[1]), 1) + self.assertEqual(len(authzr), 1) @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") def test_name3_tls_sni_01_3(self, mock_poll): @@ -138,7 +136,7 @@ class GetAuthorizationsTest(unittest.TestCase): mock_poll.side_effect = self._validate_all - authzr, _ = self.handler.get_authorizations(["0", "1", "2"]) + authzr = self.handler.get_authorizations(["0", "1", "2"]) self.assertEqual(self.mock_net.answer_challenge.call_count, 3) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 780c109c5..1f725a0a1 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -124,7 +124,7 @@ class ClientTest(unittest.TestCase): self.eg_domains, self.config.allow_subset_of_names) - authzr, _ = self.client.auth_handler.get_authorizations() + authzr = self.client.auth_handler.get_authorizations() self.acme.request_issuance.assert_called_once_with( jose.ComparableX509(OpenSSL.crypto.load_certificate_request( @@ -158,7 +158,7 @@ class ClientTest(unittest.TestCase): self.assertRaises(errors.ConfigurationError, cli.HelpfulArgumentParser.handle_csr, mock_parser, mock_parsed_args) - authzr, _ = self.client.auth_handler.get_authorizations(self.eg_domains, False) + authzr = self.client.auth_handler.get_authorizations(self.eg_domains, False) self.assertEqual( (mock.sentinel.certr, mock.sentinel.chain), @@ -190,7 +190,17 @@ class ClientTest(unittest.TestCase): # return_value is essentially set to (None, None) in # _mock_obtain_certificate(), which breaks this test. # Thus fixed by the next line. - self.client.auth_handler.get_authorizations.return_value = (None, domains) + + authzr = [] + + for domain in domains: + authzr.append( + mock.MagicMock( + body=mock.MagicMock( + identifier=mock.MagicMock( + value=domain)))) + + self.client.auth_handler.get_authorizations.return_value = authzr self.assertEqual( self.client.obtain_certificate(domains), From 1fbfbda33c2ac04e848ffcf5611e58ec68cad1cf Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 17 Mar 2016 15:43:40 -0700 Subject: [PATCH 052/317] Address review nits --- letsencrypt/main.py | 3 ++- letsencrypt/tests/display/util_test.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 264f7625e..d82122481 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -11,6 +11,8 @@ import traceback import OpenSSL import zope.component +from acme import jose + import letsencrypt from letsencrypt import account @@ -27,7 +29,6 @@ from letsencrypt import log from letsencrypt import reporter from letsencrypt import storage -from acme import jose from letsencrypt.cli import choose_configurator_plugins, _renewal_conf_files, should_renew from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco diff --git a/letsencrypt/tests/display/util_test.py b/letsencrypt/tests/display/util_test.py index 3f8ee8bb5..a16eb544e 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/letsencrypt/tests/display/util_test.py @@ -8,6 +8,7 @@ import letsencrypt.errors as errors from letsencrypt.display import util as display_util + CHOICES = [("First", "Description1"), ("Second", "Description2")] TAGS = ["tag1", "tag2", "tag3"] TAGS_CHOICES = [("1", "tag1"), ("2", "tag2"), ("3", "tag3")] From 5292e3963bc6e113c040bce6dae937bda2e5fae9 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 17 Mar 2016 16:01:40 -0700 Subject: [PATCH 053/317] Use urn:acme:error:invalidEmail when checking for e-mail errors --- letsencrypt/client.py | 3 +-- letsencrypt/tests/client_test.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 58e9c8e9f..3d611f856 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -146,8 +146,7 @@ def perform_registration(acme, config): try: return acme.register(messages.NewRegistration.from_data(email=config.email)) except messages.Error as e: - err = repr(e) - if "MX record" in err or "Validation of contact mailto" in err: + if e.typ == "urn:acme:error:invalidEmail": config.namespace.email = display_ops.get_email(more=True, invalid=True) return perform_registration(acme, config) else: diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 7dd513e18..1395a4f8f 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -63,8 +63,8 @@ class RegisterTest(unittest.TestCase): @mock.patch("letsencrypt.client.display_ops.get_email") def test_email_retry(self, _rep, mock_get_email): from acme import messages - msg = "Validation of contact mailto:sousaphone@improbablylongggstring.tld failed" - mx_err = messages.Error(detail=msg, typ="malformed", title="title") + msg = "DNS problem: NXDOMAIN looking up MX for example.com" + mx_err = messages.Error(detail=msg, typ="urn:acme:error:invalidEmail") with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: mock_client().register.side_effect = [mx_err, mock.MagicMock()] self._call() From 0847c73fd8c6626e3d64f99930cef934224a1649 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 17 Mar 2016 16:22:29 -0700 Subject: [PATCH 054/317] Fix dependency issue --- letsencrypt/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 9bbbe923a..0ba5d321e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -355,10 +355,11 @@ class HelpfulArgumentParser(object): def __init__(self, args, plugins, detect_defaults=False): from letsencrypt import main + from letsencrypt import renew self.VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, "config_changes": main.config_changes, "run": main.run, "install": main.install, "plugins": main.plugins_cmd, - "renew": renew, "revoke": main.revoke, + "renew": renew.renew, "revoke": main.revoke, "rollback": main.rollback, "everything": main.run} # List of topics for which additional help can be provided From d508a47e516d85450a47d3b763e62e3ee1692ee4 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 20 Mar 2016 18:08:53 +0200 Subject: [PATCH 055/317] Added IPv6 address to Apache test data vhost --- .../two_vhost_80/apache2/sites-available/000-default.conf | 2 +- letsencrypt-apache/letsencrypt_apache/tests/util.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf index c759768c5..2bd4e1fe9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf @@ -1,4 +1,4 @@ - + ServerName ip-172-30-0-17 ServerAdmin webmaster@localhost diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/letsencrypt-apache/letsencrypt_apache/tests/util.py index fb1e1442d..5bcb6aca6 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/util.py @@ -133,8 +133,9 @@ def get_vh_truth(temp_dir, config_name): obj.VirtualHost( os.path.join(prefix, "000-default.conf"), os.path.join(aug_pre, "000-default.conf/VirtualHost"), - set([obj.Addr.fromstring("*:80")]), False, True, - "ip-172-30-0-17"), + set([obj.Addr.fromstring("*:80"), + obj.Addr.fromstring("[::]:80")]), + False, True, "ip-172-30-0-17"), obj.VirtualHost( os.path.join(prefix, "letsencrypt.conf"), os.path.join(aug_pre, "letsencrypt.conf/VirtualHost"), From 8fbe7de625eea585384f45be3810363cd099207e Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 20 Mar 2016 18:09:43 +0200 Subject: [PATCH 056/317] Added IPv6 normalization and comparison to Addr object --- letsencrypt/plugins/common.py | 48 +++++++++++++++++++++++++++--- letsencrypt/plugins/common_test.py | 8 +++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index b72c57c7b..0b8ab635a 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -104,8 +104,9 @@ class Addr(object): :param str port: port number or \*, or "" """ - def __init__(self, tup): + def __init__(self, tup, ipv6=False): self.tup = tup + self.ipv6 = ipv6 @classmethod def fromstring(cls, str_addr): @@ -117,7 +118,7 @@ class Addr(object): port = '' if len(str_addr) > endIndex + 2 and str_addr[endIndex + 1] == ':': port = str_addr[endIndex + 2:] - return cls((host, port)) + return cls((host, port), True) else: tup = str_addr.partition(':') return cls((tup[0], tup[2])) @@ -129,7 +130,15 @@ class Addr(object): def __eq__(self, other): if isinstance(other, self.__class__): - return self.tup == other.tup + if self.ipv6: + # import ipdb;ipdb.set_trace() + return (other.ipv6 and + self._normalize_ipv6(self.tup[0]) == + self._normalize_ipv6(other.tup[0]) and + self.tup[1] == other.tup[1]) + else: + return self.tup == other.tup + return False def __hash__(self): @@ -145,7 +154,38 @@ class Addr(object): def get_addr_obj(self, port): """Return new address object with same addr and new port.""" - return self.__class__((self.tup[0], port)) + return self.__class__((self.tup[0], port), self.ipv6) + + def _normalize_ipv6(self, addr): + """Return IPv6 address in normalized form, helper function""" + addr = addr.lstrip("[") + addr = addr.rstrip("]") + return self._explode_ipv6(addr) + + def get_ipv6_exploded(self): + """Return IPv6 in normalized form""" + if self.ipv6: + return ":".join(self._normalize_ipv6(self.tup[0])) + return "" + + def _explode_ipv6(self, addr): + """Explode IPv6 address for comparison""" + result = ['0', '0', '0', '0', '0', '0', '0', '0'] + addr_list = addr.split(":") + append_to_end = False + for i in range(0, len(addr_list)): + block = addr_list[i] + if len(block) == 0: + append_to_end = True + continue + elif len(block) > 1: + # remove trailing zeros + block = block.lstrip("0") + if not append_to_end: + result[i] = str(block) + else: + result[i-len(addr_list)] = str(block) + return result class TLSSNI01(object): diff --git a/letsencrypt/plugins/common_test.py b/letsencrypt/plugins/common_test.py index 5fdd57c5f..9021e7e42 100644 --- a/letsencrypt/plugins/common_test.py +++ b/letsencrypt/plugins/common_test.py @@ -98,6 +98,10 @@ class AddrTest(unittest.TestCase): self.assertEqual(self.addr5.get_port(), "*") self.assertEqual(self.addr6.get_addr(), "[fe00::1]") self.assertEqual(self.addr6.get_port(), "80") + self.assertEqual(self.addr6.get_ipv6_exploded(), + "fe00:0:0:0:0:0:0:1") + self.assertEqual(self.addr1.get_ipv6_exploded(), + "") def test_str(self): self.assertEqual(str(self.addr1), "192.168.1.1") @@ -123,6 +127,10 @@ class AddrTest(unittest.TestCase): self.assertEqual(self.addr4, self.addr4.get_addr_obj("")) self.assertNotEqual(self.addr4, self.addr5) self.assertFalse(self.addr4 == 3333) + from letsencrypt.plugins.common import Addr + self.assertEqual(self.addr4, Addr.fromstring("[fe00:0:0::1]")) + self.assertEqual(self.addr4, Addr.fromstring("[fe00:0::0:0:1]")) + def test_set_inclusion(self): from letsencrypt.plugins.common import Addr From 092a1ee0fb8385e247ae45f785112a317183eb16 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 20 Mar 2016 18:37:57 +0200 Subject: [PATCH 057/317] Made apache configs a bit more generic for tests --- .../apache-conf-files/passing/ipv6-1143.conf | 12 +++++----- .../apache-conf-files/passing/ipv6-1143b.conf | 18 +++++++------- .../apache-conf-files/passing/ipv6-1143c.conf | 12 +++++----- .../apache-conf-files/passing/ipv6-1143d.conf | 24 ++++++++++++++----- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf index ab4ed412e..22b39c9f2 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf @@ -1,9 +1,9 @@ - -DocumentRoot /xxxx/ -ServerName noodles.net.nz -ServerAlias www.noodles.net.nz -CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined - + +DocumentRoot /var/www/html/ +ServerName example.com +ServerAlias www.example.com +CustomLog ${APACHE_LOG_DIR}/example.log combined + AllowOverride All diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf index 25655a07c..4df497ab2 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf @@ -1,10 +1,9 @@ - - -DocumentRoot /xxxx/ -ServerName noodles.net.nz -ServerAlias www.noodles.net.nz -CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined - + +DocumentRoot /var/www/html/ +ServerName example.com +ServerAlias www.example.com +CustomLog ${APACHE_LOG_DIR}/example.log combined + AllowOverride All @@ -14,8 +13,9 @@ CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined SSLProtocol all -SSLv2 -SSLv3 SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH +aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS" - SSLCertificateFile /xxxx/noodles.net.nz.crt - SSLCertificateKeyFile /xxxx/noodles.net.nz.key + SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem + SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key + Header set Strict-Transport-Security "max-age=31536000; preload" diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf index f75dd7850..40670b336 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf @@ -1,9 +1,9 @@ - -DocumentRoot /xxxx/ -ServerName noodles.net.nz -ServerAlias www.noodles.net.nz -CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined - + +DocumentRoot /var/www/html/ +ServerName example.com +ServerAlias www.example.com +CustomLog ${APACHE_LOG_DIR}/example.log combined + AllowOverride All diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf index f16b412da..813c41b62 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf @@ -1,9 +1,21 @@ - -DocumentRoot /xxxx/ -ServerName noodles.net.nz -ServerAlias www.noodles.net.nz -CustomLog ${APACHE_LOG_DIR}/domlogs/noodles.log combined - + +DocumentRoot /var/www/html/ +ServerName example.com +ServerAlias www.example.com +CustomLog ${APACHE_LOG_DIR}/example.log combined + AllowOverride All + + SSLEngine on + + SSLHonorCipherOrder On + SSLProtocol all -SSLv2 -SSLv3 + SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH +aRSA RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS" + + SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem + SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key + + + Header set Strict-Transport-Security "max-age=31536000; preload" From 9f1504eecd54bad05ca60617a9d4f67a0859bfac Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 20 Mar 2016 20:34:54 +0200 Subject: [PATCH 058/317] Trying to please Travis --- .../tests/apache-conf-files/passing/ipv6-1143.conf | 4 ++-- .../tests/apache-conf-files/passing/ipv6-1143b.conf | 7 ++----- .../tests/apache-conf-files/passing/ipv6-1143c.conf | 4 ++-- .../tests/apache-conf-files/passing/ipv6-1143d.conf | 7 ++----- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf index 22b39c9f2..ad988dc05 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf @@ -1,9 +1,9 @@ -DocumentRoot /var/www/html/ +DocumentRoot /tmp ServerName example.com ServerAlias www.example.com CustomLog ${APACHE_LOG_DIR}/example.log combined - + AllowOverride All diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf index 4df497ab2..e2b4fd3da 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf @@ -1,9 +1,9 @@ -DocumentRoot /var/www/html/ +DocumentRoot /tmp ServerName example.com ServerAlias www.example.com CustomLog ${APACHE_LOG_DIR}/example.log combined - + AllowOverride All @@ -15,7 +15,4 @@ CustomLog ${APACHE_LOG_DIR}/example.log combined SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key - - - Header set Strict-Transport-Security "max-age=31536000; preload" diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf index 40670b336..f2d2ecbea 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf @@ -1,9 +1,9 @@ -DocumentRoot /var/www/html/ +DocumentRoot /tmp ServerName example.com ServerAlias www.example.com CustomLog ${APACHE_LOG_DIR}/example.log combined - + AllowOverride All diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf index 813c41b62..f5b7a2b45 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf @@ -1,9 +1,9 @@ -DocumentRoot /var/www/html/ +DocumentRoot /tmp ServerName example.com ServerAlias www.example.com CustomLog ${APACHE_LOG_DIR}/example.log combined - + AllowOverride All @@ -15,7 +15,4 @@ CustomLog ${APACHE_LOG_DIR}/example.log combined SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key - - - Header set Strict-Transport-Security "max-age=31536000; preload" From bbbfe80b94545bb366fef27eea53edcd665f0c0c Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 20 Mar 2016 21:57:52 +0200 Subject: [PATCH 059/317] Changed testdata directory names and TwoVhost80Test to a better fitting one --- .../tests/augeas_configurator_test.py | 2 +- .../letsencrypt_apache/tests/configurator_test.py | 6 +++--- .../letsencrypt_apache/tests/display_ops_test.py | 2 +- .../letsencrypt_apache/tests/parser_test.py | 2 +- .../apache2/apache2.conf | 0 .../apache2/conf-available/bad_conf_file.conf | 0 .../conf-available/other-vhosts-access-log.conf | 0 .../apache2/conf-available/security.conf | 0 .../apache2/conf-available/serve-cgi-bin.conf | 0 .../conf-enabled/other-vhosts-access-log.conf | 0 .../apache2/conf-enabled/security.conf | 0 .../apache2/conf-enabled/serve-cgi-bin.conf | 0 .../apache2/envvars | 0 .../apache2/mods-available/authz_svn.load | 0 .../apache2/mods-available/dav.load | 0 .../apache2/mods-available/dav_svn.conf | 0 .../apache2/mods-available/dav_svn.load | 0 .../apache2/mods-available/rewrite.load | 0 .../apache2/mods-available/ssl.conf | 0 .../apache2/mods-available/ssl.load | 0 .../apache2/mods-enabled/.gitignore | 0 .../apache2/mods-enabled/authz_svn.load | 0 .../apache2/mods-enabled/dav.load | 0 .../apache2/mods-enabled/dav_svn.conf | 0 .../apache2/mods-enabled/dav_svn.load | 0 .../apache2/ports.conf | 0 .../apache2/sites-available/000-default.conf | 0 .../sites-available/default-ssl-port-only.conf | 0 .../apache2/sites-available/default-ssl.conf | 0 .../sites-available/encryption-example.conf | 0 .../apache2/sites-available/letsencrypt.conf | 0 .../apache2/sites-available/mod_macro-example.conf | 0 .../apache2/sites-available/wildcard.conf | 0 .../apache2/sites-enabled/000-default.conf | 0 .../apache2/sites-enabled/encryption-example.conf | 0 .../apache2/sites-enabled/letsencrypt.conf | 0 .../apache2/sites-enabled/mod_macro-example.conf | 0 .../{two_vhost_80 => multiple_vhosts}/sites | 0 .../letsencrypt_apache/tests/util.py | 14 +++++++------- 39 files changed, 13 insertions(+), 13 deletions(-) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/apache2.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-available/bad_conf_file.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-available/other-vhosts-access-log.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-available/security.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-available/serve-cgi-bin.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-enabled/other-vhosts-access-log.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-enabled/security.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/conf-enabled/serve-cgi-bin.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/envvars (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/authz_svn.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/dav.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/dav_svn.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/dav_svn.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/rewrite.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/ssl.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-available/ssl.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-enabled/.gitignore (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-enabled/authz_svn.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-enabled/dav.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-enabled/dav_svn.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/mods-enabled/dav_svn.load (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/ports.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/000-default.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/default-ssl-port-only.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/default-ssl.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/encryption-example.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/letsencrypt.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/mod_macro-example.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-available/wildcard.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-enabled/000-default.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-enabled/encryption-example.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-enabled/letsencrypt.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/apache2/sites-enabled/mod_macro-example.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/{two_vhost_80 => multiple_vhosts}/sites (100%) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py index b70e1c7f1..bf95f72ce 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py @@ -20,7 +20,7 @@ class AugeasConfiguratorTest(util.ApacheTest): self.config_path, self.vhost_path, self.config_dir, self.work_dir) self.vh_truth = util.get_vh_truth( - self.temp_dir, "debian_apache_2_4/two_vhost_80") + self.temp_dir, "debian_apache_2_4/multiple_vhosts") def tearDown(self): shutil.rmtree(self.config_dir) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 81e237be3..927d918ae 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -20,17 +20,17 @@ from letsencrypt_apache import obj from letsencrypt_apache.tests import util -class TwoVhost80Test(util.ApacheTest): +class MultipleVhostsTest(util.ApacheTest): """Test two standard well-configured HTTP vhosts.""" def setUp(self): # pylint: disable=arguments-differ - super(TwoVhost80Test, self).setUp() + super(MultipleVhostsTest, self).setUp() self.config = util.get_apache_configurator( self.config_path, self.vhost_path, self.config_dir, self.work_dir) self.config = self.mock_deploy_cert(self.config) self.vh_truth = util.get_vh_truth( - self.temp_dir, "debian_apache_2_4/two_vhost_80") + self.temp_dir, "debian_apache_2_4/multiple_vhosts") def mock_deploy_cert(self, config): """A test for a mock deploy cert""" diff --git a/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py b/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py index f7fbac947..124ba2f17 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py @@ -20,7 +20,7 @@ class SelectVhostTest(unittest.TestCase): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) self.base_dir = "/example_path" self.vhosts = util.get_vh_truth( - self.base_dir, "debian_apache_2_4/two_vhost_80") + self.base_dir, "debian_apache_2_4/multiple_vhosts") @classmethod def _call(cls, vhosts): diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py index 2e6481aba..f4d4660c9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py @@ -193,7 +193,7 @@ class ParserInitTest(util.ApacheTest): "update_runtime_variables"): path = os.path.join( self.temp_dir, - "debian_apache_2_4/////two_vhost_80/../two_vhost_80/apache2") + "debian_apache_2_4/////multiple_vhosts/../multiple_vhosts/apache2") parser = ApacheParser(self.aug, path, "/dummy/vhostpath") diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/bad_conf_file.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/bad_conf_file.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/authz_svn.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/authz_svn.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/dav.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/dav.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/dav_svn.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/dav_svn.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/dav_svn.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/dav_svn.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/rewrite.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/rewrite.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/.gitignore b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/.gitignore rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/authz_svn.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/authz_svn.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/dav.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/dav.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/dav_svn.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/dav_svn.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/dav_svn.load b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-enabled/dav_svn.load rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl-port-only.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl-port-only.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/mod_macro-example.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/mod_macro-example.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/wildcard.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/wildcard.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/mod_macro-example.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/mod_macro-example.conf rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/sites b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/two_vhost_80/sites rename to letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/letsencrypt-apache/letsencrypt_apache/tests/util.py index fb1e1442d..928084e3c 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/util.py @@ -22,9 +22,9 @@ from letsencrypt_apache import obj class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods - def setUp(self, test_dir="debian_apache_2_4/two_vhost_80", - config_root="debian_apache_2_4/two_vhost_80/apache2", - vhost_root="debian_apache_2_4/two_vhost_80/apache2/sites-available"): + def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts", + config_root="debian_apache_2_4/multiple_vhosts/apache2", + vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"): # pylint: disable=arguments-differ super(ApacheTest, self).setUp() @@ -59,9 +59,9 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods - def setUp(self, test_dir="debian_apache_2_4/two_vhost_80", - config_root="debian_apache_2_4/two_vhost_80/apache2", - vhost_root="debian_apache_2_4/two_vhost_80/apache2/sites-available"): + def setUp(self, test_dir="debian_apache_2_4/multiple_vhosts", + config_root="debian_apache_2_4/multiple_vhosts/apache2", + vhost_root="debian_apache_2_4/multiple_vhosts/apache2/sites-available"): super(ParserTest, self).setUp(test_dir, config_root, vhost_root) zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -116,7 +116,7 @@ def get_apache_configurator( def get_vh_truth(temp_dir, config_name): """Return the ground truth for the specified directory.""" - if config_name == "debian_apache_2_4/two_vhost_80": + if config_name == "debian_apache_2_4/multiple_vhosts": prefix = os.path.join( temp_dir, config_name, "apache2/sites-available") aug_pre = "/files" + prefix From 7675d50f4cf2addeb1995db8fcbba0cc53802609 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Sun, 20 Mar 2016 22:29:01 +0200 Subject: [PATCH 060/317] Cleaning up debugging leftovers --- letsencrypt/plugins/common.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 0b8ab635a..9b3bee7ef 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -131,7 +131,6 @@ class Addr(object): def __eq__(self, other): if isinstance(other, self.__class__): if self.ipv6: - # import ipdb;ipdb.set_trace() return (other.ipv6 and self._normalize_ipv6(self.tup[0]) == self._normalize_ipv6(other.tup[0]) and From 6f25005559ce104a72ff2016438ae57ab249048a Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Mon, 21 Mar 2016 01:12:59 +0200 Subject: [PATCH 061/317] Adding tests --- letsencrypt/auth_handler.py | 7 +++---- letsencrypt/cli.py | 3 +++ letsencrypt/tests/auth_handler_test.py | 3 +++ letsencrypt/tests/cli_test.py | 4 ++++ 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 1d82705b7..284f9affd 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -66,7 +66,6 @@ class AuthHandler(object): self._choose_challenges(domains) - # While there are still challenges remaining... while self.achalls: resp = self._solve_challenges() @@ -80,11 +79,11 @@ class AuthHandler(object): # Only return valid authorizations retVal = [authzr for authzr in self.authzr.values() - if authzr.body.status == messages.STATUS_VALID] + if authzr.body.status == messages.STATUS_VALID] if len(retVal) <= 0: - logger.critical("Challenges failed for all domains") - raise + raise errors.AuthorizationError( + "Challenges failed for all domains") return retVal diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 2ccff232b..4b3b3860b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1244,6 +1244,9 @@ class HelpfulArgumentParser(object): parsed_args.register_unsafely_without_email = True if parsed_args.csr: + if parsed_args.allow_subset_of_names: + raise errors.Error("--allow-subset-of-names " + "cannot be used with --csr") self.handle_csr(parsed_args) if self.detect_defaults: # plumbing diff --git a/letsencrypt/tests/auth_handler_test.py b/letsencrypt/tests/auth_handler_test.py index 61dd0e21b..b7ac04984 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/letsencrypt/tests/auth_handler_test.py @@ -163,6 +163,9 @@ class GetAuthorizationsTest(unittest.TestCase): self.assertRaises( errors.AuthorizationError, self.handler.get_authorizations, ["0"]) + def test_no_domains(self): + self.assertRaises(errors.AuthorizationError, self.handler.get_authorizations, []) + def _validate_all(self, unused_1, unused_2): for dom in self.handler.authzr.keys(): azr = self.handler.authzr[dom] diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index bfd3818c1..feb70c2e9 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -360,6 +360,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._call, ['-d', '204.11.231.35']) + def test_csr_with_besteffort(self): + args = ["--csr", CSR, "--allow-subset-of-names"] + self.assertRaises(errors.Error, self._call, args) + def test_run_with_csr(self): # This is an error because you can only use --csr with certonly try: From bd7d85fd601eab333d83bd7264bc5bc2bef4c3e9 Mon Sep 17 00:00:00 2001 From: Maikel Date: Mon, 21 Mar 2016 13:52:56 +0100 Subject: [PATCH 062/317] Fix symlink webroot paths When using symlinks for webroot folders and requesting SSL for same websites with symlink domains. The challenges files and acme-challenge directory are removed on the first domain and on second domain the acme-challenge directory is already removed and tries to remove it again. --- letsencrypt/plugins/webroot.py | 3 +++ letsencrypt/plugins/webroot_test.py | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 6d2899511..6254719ed 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -155,5 +155,8 @@ to serve all files under specified web root ({0}).""" elif exc.errno == errno.EACCES: logger.debug("Challenges cleaned up but no permissions for %s", root_path) + elif exc.errno == errno.ENOENT: + logger.debug("Challenges cleaned up, %s does not exists", + root_path) else: raise diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index ed0326555..b39e3912a 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -176,12 +176,25 @@ class AuthenticatorTest(unittest.TestCase): self.auth.perform([self.achall]) os_error = OSError() - os_error.errno = errno.ENOENT + os_error.errno = errno.EPERM mock_rmdir.side_effect = os_error self.assertRaises(OSError, self.auth.cleanup, [self.achall]) self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) + @mock.patch('os.rmdir') + def test_cleanup_file_not_exists(self, mock_rmdir): + self.auth.prepare() + self.auth.perform([self.achall]) + + os_error = OSError() + os_error.errno = errno.ENOENT + mock_rmdir.side_effect = os_error + + self.auth.cleanup([self.achall]) + self.assertFalse(os.path.exists(self.validation_path)) + self.assertTrue(os.path.exists(self.root_challenge_path)) + if __name__ == "__main__": unittest.main() # pragma: no cover From a8350ce04a2d1e5b86f0a1931bee9cb5b297abe5 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Mon, 21 Mar 2016 23:43:38 +0200 Subject: [PATCH 063/317] Refining code and documentation --- letsencrypt/auth_handler.py | 7 +++---- letsencrypt/client.py | 6 +++--- letsencrypt/tests/client_test.py | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/letsencrypt/auth_handler.py b/letsencrypt/auth_handler.py index 284f9affd..658315597 100644 --- a/letsencrypt/auth_handler.py +++ b/letsencrypt/auth_handler.py @@ -52,9 +52,8 @@ class AuthHandler(object): :param bool best_effort: Whether or not all authorizations are required (this is useful in renewal) - :returns: tuple of lists of authorization resources. Takes the - form of (`completed`, `failed`) - :rtype: tuple + :returns: List of authorization resources + :rtype: list :raises .AuthorizationError: If unable to retrieve all authorizations @@ -81,7 +80,7 @@ class AuthHandler(object): retVal = [authzr for authzr in self.authzr.values() if authzr.body.status == messages.STATUS_VALID] - if len(retVal) <= 0: + if not retVal: raise errors.AuthorizationError( "Challenges failed for all domains") diff --git a/letsencrypt/client.py b/letsencrypt/client.py index f2c3d1636..30df6147f 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -199,7 +199,7 @@ class Client(object): :param .le_util.CSR csr: DER-encoded Certificate Signing Request. The key used to generate this CSR can be different than `authkey`. - :param dict authzr: ACME Authorization Resource dict where keys are + :param list authzr: ACME Authorization Resource dict where keys are domains and values are :class:`acme.messages.AuthorizationResource` :returns: `.CertificateResource` and certificate chain (as @@ -244,8 +244,8 @@ class Client(object): domains, self.config.allow_subset_of_names) - domains = [a.body.identifier.value.encode('ascii', 'ignore') - for a in authzr] + domains = [a.body.identifier.value.encode('ascii') + for a in authzr] # Create CSR from names key = crypto_util.init_save_key( diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 1f725a0a1..33e0f1a11 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -115,7 +115,7 @@ class ClientTest(unittest.TestCase): def _mock_obtain_certificate(self): self.client.auth_handler = mock.MagicMock() - self.client.auth_handler.get_authorizations.return_value = (None, None) + self.client.auth_handler.get_authorizations.return_value = [None] self.acme.request_issuance.return_value = mock.sentinel.certr self.acme.fetch_chain.return_value = mock.sentinel.chain From 838eb28d0a2f412249df86d2fcf25a72b4a82169 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Tue, 22 Mar 2016 00:00:51 +0200 Subject: [PATCH 064/317] Adding test for obtain_certificate_from_csr with authzr=None --- letsencrypt/tests/client_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 33e0f1a11..f1bc3de25 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -169,6 +169,17 @@ class ClientTest(unittest.TestCase): # and that the cert was obtained correctly self._check_obtain_certificate() + # Test for authzr=None + self.assertEqual( + (mock.sentinel.certr, mock.sentinel.chain), + self.client.obtain_certificate_from_csr( + self.eg_domains, + test_csr, + authzr=None)) + + self.client.auth_handler.get_authorizations.assert_called_with( + self.eg_domains) + # Test for no auth_handler self.client.auth_handler = None self.assertRaises( From dc564efd0cabe6d3e7f4cb5553fb445a9a1213d0 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Tue, 22 Mar 2016 00:38:59 +0200 Subject: [PATCH 065/317] Refining obtain_certificate_from_csr docstring --- letsencrypt/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 30df6147f..1345a7f23 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -199,8 +199,8 @@ class Client(object): :param .le_util.CSR csr: DER-encoded Certificate Signing Request. The key used to generate this CSR can be different than `authkey`. - :param list authzr: ACME Authorization Resource dict where keys are - domains and values are :class:`acme.messages.AuthorizationResource` + :param list authzr: List of + :class:`acme.messages.AuthorizationResource` :returns: `.CertificateResource` and certificate chain (as returned by `.fetch_chain`). From 432a85cd18f4b876434fd75d4a65f09222c7029a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 21 Mar 2016 16:19:57 -0700 Subject: [PATCH 066/317] Add Ncurses directory_select --- letsencrypt/display/util.py | 24 ++++++++++++++++++++++++ letsencrypt/tests/display/util_test.py | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/letsencrypt/display/util.py b/letsencrypt/display/util.py index 84049c47c..3913f8bf1 100644 --- a/letsencrypt/display/util.py +++ b/letsencrypt/display/util.py @@ -11,6 +11,13 @@ from letsencrypt import errors WIDTH = 72 HEIGHT = 20 +DSELECT_HELP = ( + "Use the arrow keys or tab to move between window elements. Space can be " + "used to complete the input path with the selected element in the " + "directory window. Pressing enter will select the currently highlighted " + "button.") +"""Help text on how to use dialog's dselect.""" + # Display exit codes OK = "ok" """Display exit code indicating user acceptance.""" @@ -21,6 +28,7 @@ CANCEL = "cancel" HELP = "help" """Display exit code when for when the user requests more help.""" + def _wrap_lines(msg): """Format lines nicely to 80 chars. @@ -36,6 +44,7 @@ def _wrap_lines(msg): fixed_l.append(textwrap.fill(line, 80)) return os.linesep.join(fixed_l) + @zope.interface.implementer(interfaces.IDisplay) class NcursesDisplay(object): """Ncurses-based display.""" @@ -174,6 +183,21 @@ class NcursesDisplay(object): return self.dialog.checklist( message, width=self.width, height=self.height, choices=choices) + def directory_select(self, message, **unused_kwargs): + """Display a directory selection screen. + + :param str message: prompt to give the user + + :returns: tuple of the form (`code`, `string`) where + `code` - int display exit code + `string` - input entered by the user + + """ + root_directory = os.path.abspath(os.sep) + return self.dialog.dselect( + filepath=root_directory, width=self.width, + height=self.height, help_button=True, title=message) + @zope.interface.implementer(interfaces.IDisplay) class FileDisplay(object): diff --git a/letsencrypt/tests/display/util_test.py b/letsencrypt/tests/display/util_test.py index a16eb544e..45025d7a0 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/letsencrypt/tests/display/util_test.py @@ -123,6 +123,11 @@ class NcursesDisplayTest(unittest.TestCase): "message", width=display_util.WIDTH, height=display_util.HEIGHT, choices=choices) + @mock.patch("letsencrypt.display.util.dialog.Dialog.dselect") + def test_directory_select(self, mock_dselect): + self.displayer.directory_select("message") + self.assertEqual(mock_dselect.call_count, 1) + class FileOutputDisplayTest(unittest.TestCase): """Test stdout display. From 08232eef43680e189aaf2776f0953c2a22bd5dfe Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 21 Mar 2016 16:22:15 -0700 Subject: [PATCH 067/317] display.util pep8 cleanup --- letsencrypt/display/util.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/letsencrypt/display/util.py b/letsencrypt/display/util.py index 3913f8bf1..05e280118 100644 --- a/letsencrypt/display/util.py +++ b/letsencrypt/display/util.py @@ -127,7 +127,6 @@ class NcursesDisplay(object): return code, int(index) - 1 - def input(self, message, **unused_kwargs): """Display an input box to the user. @@ -141,11 +140,10 @@ class NcursesDisplay(object): """ sections = message.split("\n") # each section takes at least one line, plus extras if it's longer than self.width - wordlines = [1 + (len(section)/self.width) for section in sections] + wordlines = [1 + (len(section) / self.width) for section in sections] height = 6 + sum(wordlines) + len(sections) return self.dialog.inputbox(message, width=self.width, height=height) - def yesno(self, message, yes_label="Yes", no_label="No", **unused_kwargs): """Display a Yes/No dialog box. @@ -397,7 +395,6 @@ class FileDisplay(object): self.outfile.write(side_frame) - def _get_valid_int_ans(self, max_): """Get a numerical selection. @@ -433,6 +430,7 @@ class FileDisplay(object): return OK, selection + @zope.interface.implementer(interfaces.IDisplay) class NoninteractiveDisplay(object): """An iDisplay implementation that never asks for interactive user input""" @@ -507,7 +505,6 @@ class NoninteractiveDisplay(object): else: return OK, default - def yesno(self, message, yes_label=None, no_label=None, default=None, cli_flag=None): # pylint: disable=unused-argument """Decide Yes or No, without asking anybody From 95a4a2ca6098f23322f7ed8e30c218d198e52903 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 21 Mar 2016 16:23:21 -0700 Subject: [PATCH 068/317] display.util_test cleanup --- letsencrypt/tests/display/util_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/letsencrypt/tests/display/util_test.py b/letsencrypt/tests/display/util_test.py index 45025d7a0..adec265dc 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/letsencrypt/tests/display/util_test.py @@ -285,6 +285,7 @@ class FileOutputDisplayTest(unittest.TestCase): self.displayer._get_valid_int_ans(3), (display_util.CANCEL, -1)) + class NoninteractiveDisplayTest(unittest.TestCase): """Test non-interactive display. From 0fd453fd5e16c08bed271010539be61d874aa131 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 22 Mar 2016 10:23:14 +0200 Subject: [PATCH 069/317] Added code comments to clarify inner workings --- letsencrypt/plugins/common.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 9b3bee7ef..79c95e149 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -131,6 +131,8 @@ class Addr(object): def __eq__(self, other): if isinstance(other, self.__class__): if self.ipv6: + # compare normalized to take different + # styles of representation into account return (other.ipv6 and self._normalize_ipv6(self.tup[0]) == self._normalize_ipv6(other.tup[0]) and @@ -175,14 +177,17 @@ class Addr(object): for i in range(0, len(addr_list)): block = addr_list[i] if len(block) == 0: + # encountered ::, so rest of the blocks should be + # appended to the end append_to_end = True continue elif len(block) > 1: - # remove trailing zeros + # remove leading zeros block = block.lstrip("0") if not append_to_end: result[i] = str(block) else: + # count the location from the end using negative indices result[i-len(addr_list)] = str(block) return result From 2d211c7aef4951e9147d35369e96b2e522c9f233 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Tue, 22 Mar 2016 12:44:40 -0700 Subject: [PATCH 070/317] Mention that we're talking about Unix OSes --- README.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 874b0c450..75de094a8 100644 --- a/README.rst +++ b/README.rst @@ -23,11 +23,11 @@ configuring webservers to use them. Installation ------------ -If ``letsencrypt`` is packaged for your OS, you can install it from there, and -run it by typing ``letsencrypt``. Because not all operating systems have -packages yet, we provide a temporary solution via the ``letsencrypt-auto`` -wrapper script, which obtains some dependencies from your OS and puts others -in a python virtual environment:: +If ``letsencrypt`` is packaged for your Unix OS, you can install it from +there, and run it by typing ``letsencrypt``. Because not all operating +systems have packages yet, we provide a temporary solution via the +``letsencrypt-auto`` wrapper script, which obtains some dependencies +from your OS and puts others in a python virtual environment:: user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt user@webserver:~$ cd letsencrypt From 46ef3e6374358dc65c0ee07fccc82332b7af5a20 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Tue, 22 Mar 2016 12:49:39 -0700 Subject: [PATCH 071/317] More explicit --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 75de094a8..050cde82b 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,8 @@ The Let's Encrypt Client is a fully-featured, extensible client for the Let's Encrypt CA (or any other CA that speaks the `ACME `_ protocol) that can automate the tasks of obtaining certificates and -configuring webservers to use them. +configuring webservers to use them. This client runs on Unix-based operating +systems. Installation ------------ From 74a7d2bed90a3ecadf73f723f913c6a6e4f0553b Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 22 Mar 2016 18:07:51 -0700 Subject: [PATCH 072/317] Added completer.py and tests for FileDisplay --- letsencrypt/display/completer.py | 51 ++++++++++++++++ letsencrypt/tests/display/completer_test.py | 64 +++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 letsencrypt/display/completer.py create mode 100644 letsencrypt/tests/display/completer_test.py diff --git a/letsencrypt/display/completer.py b/letsencrypt/display/completer.py new file mode 100644 index 000000000..5258ef52e --- /dev/null +++ b/letsencrypt/display/completer.py @@ -0,0 +1,51 @@ +"""Provides tab autocompletion when prompting users for a path.""" +import glob +import readline + + +class Completer(object): + """Provides tab autocompletion when prompting users for a path. + + This class is meant to be used with readline to provide tab + autocompletion for users entering paths. The complete method can + be passed to readline.set_completer directly, however, this function + works best as a context manager. For example: + + with Completer(): + raw_input() + + In this example, tab autocompletion will be available during + the call to raw_input above, however, readline will be restored to + its previous state when exiting the body of the with statement. + + """ + + def __init__(self): + self._completer = self._delims = self._iter = None + + def complete(self, text, state): + """Provides path autocompletion for use with readline. + + :param str text: text to offer completions for + :param int state: which completion to return + + :returns: possible completion for text or ``None`` if all + completions have been returned + :rtype: str + + """ + if state == 0: + self._iter = glob.iglob(text + '*') + return next(self._iter, None) + + def __enter__(self): + self._completer = readline.get_completer() + self._delims = readline.get_completer_delims() + + readline.set_completer(self.complete) + readline.set_completer_delims(' \t\n;') + readline.parse_and_bind('tab: complete') + + def __exit__(self, unused_type, unused_value, unused_traceback): + readline.set_completer_delims(self._delims) + readline.set_completer(self._completer) diff --git a/letsencrypt/tests/display/completer_test.py b/letsencrypt/tests/display/completer_test.py new file mode 100644 index 000000000..dbba15106 --- /dev/null +++ b/letsencrypt/tests/display/completer_test.py @@ -0,0 +1,64 @@ +"""Test letsencrypt.display.completer.""" +import os +import readline +import shutil +import string +import tempfile +import unittest + + +class CompleterTest(unittest.TestCase): + """Test letsencrypt.display.completer.Completer.""" + + def setUp(self): + self.temp_dir = tempfile.mkdtemp() + + # directories must end with os.sep for completer to + # search inside the directory for possible completions + if self.temp_dir[-1] != os.sep: + self.temp_dir += os.sep + + self.paths = [] + # create some files and directories in temp_dir + for c in string.ascii_lowercase: + path = os.path.join(self.temp_dir, c) + self.paths.append(path) + if ord(c) % 2: + os.mkdir(path) + else: + with open(path, 'w'): + pass + + def tearDown(self): + shutil.rmtree(self.temp_dir) + + def test_context_manager(self): + from letsencrypt.display import completer + + original_completer = readline.get_completer() + original_delims = readline.get_completer_delims() + + with completer.Completer(): + pass + + self.assertEqual(readline.get_completer(), original_completer) + self.assertEqual(readline.get_completer_delims(), original_delims) + + def test_complete(self): + from letsencrypt.display import completer + + my_completer = completer.Completer() + num_paths = len(self.paths) + + for i in range(num_paths): + completion = my_completer.complete(self.temp_dir, i) + self.assertTrue(completion in self.paths) + self.paths.remove(completion) + + self.assertFalse(self.paths) + completion = my_completer.complete(self.temp_dir, num_paths) + self.assertEqual(completion, None) + + +if __name__ == "__main__": + unittest.main() # pragma: no cover From da97b74d710e8411a2e0070b3c09de6cf27e6df9 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Wed, 23 Mar 2016 18:12:07 +0200 Subject: [PATCH 073/317] Fixed Gentoo define command --- letsencrypt-apache/letsencrypt_apache/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 77f71a461..ab85fb1f6 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -42,7 +42,7 @@ CLI_DEFAULTS_GENTOO = dict( vhost_root="/etc/apache2/vhosts.d", vhost_files="*.conf", version_cmd=['/usr/sbin/apache2', '-v'], - define_cmd=['/usr/sbin/apache2', '-t', '-D', 'DUMP_RUN_CFG'], + define_cmd=['apache2ctl', 'virtualhosts'], restart_cmd=['apache2ctl', 'graceful'], conftest_cmd=['apache2ctl', 'configtest'], enmod=None, From 49fefb08dd126f881d33f8c32ec5caf78acf0e2c Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 10:35:38 -0700 Subject: [PATCH 074/317] autocomplete -> complete --- letsencrypt/display/completer.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/letsencrypt/display/completer.py b/letsencrypt/display/completer.py index 5258ef52e..71e72b942 100644 --- a/letsencrypt/display/completer.py +++ b/letsencrypt/display/completer.py @@ -1,22 +1,22 @@ -"""Provides tab autocompletion when prompting users for a path.""" +"""Provides tab completion when prompting users for a path.""" import glob import readline class Completer(object): - """Provides tab autocompletion when prompting users for a path. + """Provides tab completion when prompting users for a path. This class is meant to be used with readline to provide tab - autocompletion for users entering paths. The complete method can - be passed to readline.set_completer directly, however, this function + completion for users entering paths. The complete method can be + passed to readline.set_completer directly, however, this function works best as a context manager. For example: with Completer(): raw_input() - In this example, tab autocompletion will be available during - the call to raw_input above, however, readline will be restored to - its previous state when exiting the body of the with statement. + In this example, tab completion will be available during the call to + raw_input above, however, readline will be restored to its previous + state when exiting the body of the with statement. """ @@ -24,7 +24,7 @@ class Completer(object): self._completer = self._delims = self._iter = None def complete(self, text, state): - """Provides path autocompletion for use with readline. + """Provides path completion for use with readline. :param str text: text to offer completions for :param int state: which completion to return From f9ac7d789b898353a79e74a07d93f71e63d362ee Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 11:15:37 -0700 Subject: [PATCH 075/317] Add support libedit readline --- letsencrypt/display/completer.py | 8 +++- letsencrypt/tests/display/completer_test.py | 52 ++++++++++++++++----- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/letsencrypt/display/completer.py b/letsencrypt/display/completer.py index 71e72b942..6b07a2e47 100644 --- a/letsencrypt/display/completer.py +++ b/letsencrypt/display/completer.py @@ -44,7 +44,13 @@ class Completer(object): readline.set_completer(self.complete) readline.set_completer_delims(' \t\n;') - readline.parse_and_bind('tab: complete') + + # readline can be implemented using GNU readline or libedit + # which have different configuration syntax + if 'libedit' in readline.__doc__: + readline.parse_and_bind('bind ^I rl_complete') + else: + readline.parse_and_bind('tab: complete') def __exit__(self, unused_type, unused_value, unused_traceback): readline.set_completer_delims(self._delims) diff --git a/letsencrypt/tests/display/completer_test.py b/letsencrypt/tests/display/completer_test.py index dbba15106..a77d3c842 100644 --- a/letsencrypt/tests/display/completer_test.py +++ b/letsencrypt/tests/display/completer_test.py @@ -6,6 +6,8 @@ import string import tempfile import unittest +import mock + class CompleterTest(unittest.TestCase): """Test letsencrypt.display.completer.Completer.""" @@ -32,18 +34,6 @@ class CompleterTest(unittest.TestCase): def tearDown(self): shutil.rmtree(self.temp_dir) - def test_context_manager(self): - from letsencrypt.display import completer - - original_completer = readline.get_completer() - original_delims = readline.get_completer_delims() - - with completer.Completer(): - pass - - self.assertEqual(readline.get_completer(), original_completer) - self.assertEqual(readline.get_completer_delims(), original_delims) - def test_complete(self): from letsencrypt.display import completer @@ -59,6 +49,44 @@ class CompleterTest(unittest.TestCase): completion = my_completer.complete(self.temp_dir, num_paths) self.assertEqual(completion, None) + def test_context_manager(self): + from letsencrypt.display import completer + + original_completer = readline.get_completer() + original_delims = readline.get_completer_delims() + + with completer.Completer(): + pass + + self.assertEqual(readline.get_completer(), original_completer) + self.assertEqual(readline.get_completer_delims(), original_delims) + + @mock.patch('letsencrypt.display.completer.readline', autospec=True) + def test_context_manager_libedit(self, mock_readline): + mock_readline.__doc__ = 'libedit' + self._test_mocked_readline(mock_readline) + + @mock.patch('letsencrypt.display.completer.readline', autospec=True) + def test_context_manager_readline(self, mock_readline): + mock_readline.__doc__ = 'GNU readline' + self._test_mocked_readline(mock_readline) + + def _test_mocked_readline(self, mock_readline): + from letsencrypt.display import completer + + mock_readline.parse_and_bind.side_effect = enable_tab_completion + + with completer.Completer(): + pass + + self.assertTrue(mock_readline.parse_and_bind.called) + + +def enable_tab_completion(unused_command): + """Enables readline tab completion using the system specific syntax.""" + libedit = 'libedit' in readline.__doc__ + command = 'bind ^I rl_complete' if libedit else 'tab: complete' + readline.parse_and_bind(command) if __name__ == "__main__": unittest.main() # pragma: no cover From 0c0687ca68ae4b9c4fa2585bfd1a974d121bed30 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 11:27:20 -0700 Subject: [PATCH 076/317] Add dummy_readline module --- letsencrypt/display/dummy_readline.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 letsencrypt/display/dummy_readline.py diff --git a/letsencrypt/display/dummy_readline.py b/letsencrypt/display/dummy_readline.py new file mode 100644 index 000000000..fb3d807bb --- /dev/null +++ b/letsencrypt/display/dummy_readline.py @@ -0,0 +1,21 @@ +"""A dummy module with no effect for use on systems without readline.""" + + +def get_completer(): + """An empty implementation of readline.get_completer.""" + + +def get_completer_delims(): + """An empty implementation of readline.get_completer_delims.""" + + +def parse_and_bind(unused_command): + """An empty implementation of readline.parse_and_bind.""" + + +def set_completer(unused_function=None): + """An empty implementation of readline.set_completer.""" + + +def set_completer_delims(unused_delims): + """An empty implementation of readline.set_completer_delims.""" From 7820c687f16550c7a406209918092cfbbd9c53de Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 13:33:07 -0700 Subject: [PATCH 077/317] Use dummy_readline to prevent ImportErrors from readline breaking LE --- letsencrypt/display/completer.py | 6 +++++- letsencrypt/tests/display/completer_test.py | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/letsencrypt/display/completer.py b/letsencrypt/display/completer.py index 6b07a2e47..83dafa25d 100644 --- a/letsencrypt/display/completer.py +++ b/letsencrypt/display/completer.py @@ -1,6 +1,10 @@ """Provides tab completion when prompting users for a path.""" import glob -import readline +# readline module is not available on all systems +try: + import readline +except ImportError: + import letsencrypt.display.dummy_readline as readline class Completer(object): diff --git a/letsencrypt/tests/display/completer_test.py b/letsencrypt/tests/display/completer_test.py index a77d3c842..3c181c925 100644 --- a/letsencrypt/tests/display/completer_test.py +++ b/letsencrypt/tests/display/completer_test.py @@ -3,10 +3,12 @@ import os import readline import shutil import string +import sys import tempfile import unittest import mock +from six.moves import reload_module # pylint: disable=import-error class CompleterTest(unittest.TestCase): @@ -36,7 +38,6 @@ class CompleterTest(unittest.TestCase): def test_complete(self): from letsencrypt.display import completer - my_completer = completer.Completer() num_paths = len(self.paths) @@ -49,8 +50,17 @@ class CompleterTest(unittest.TestCase): completion = my_completer.complete(self.temp_dir, num_paths) self.assertEqual(completion, None) - def test_context_manager(self): + def test_import_error(self): + original_readline = sys.modules['readline'] + sys.modules['readline'] = None + + self.test_context_manager_with_unmocked_readline() + + sys.modules['readline'] = original_readline + + def test_context_manager_with_unmocked_readline(self): from letsencrypt.display import completer + reload_module(completer) original_completer = readline.get_completer() original_delims = readline.get_completer_delims() @@ -64,14 +74,14 @@ class CompleterTest(unittest.TestCase): @mock.patch('letsencrypt.display.completer.readline', autospec=True) def test_context_manager_libedit(self, mock_readline): mock_readline.__doc__ = 'libedit' - self._test_mocked_readline(mock_readline) + self._test_context_manager_with_mock_readline(mock_readline) @mock.patch('letsencrypt.display.completer.readline', autospec=True) def test_context_manager_readline(self, mock_readline): mock_readline.__doc__ = 'GNU readline' - self._test_mocked_readline(mock_readline) + self._test_context_manager_with_mock_readline(mock_readline) - def _test_mocked_readline(self, mock_readline): + def _test_context_manager_with_mock_readline(self, mock_readline): from letsencrypt.display import completer mock_readline.parse_and_bind.side_effect = enable_tab_completion From 278b8852fd3844f66af03d3ee6586ac10aee6c32 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Mar 2016 14:26:58 -0700 Subject: [PATCH 078/317] Merge snafu --- 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 adbe72701..55a504f82 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -54,7 +54,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods shutil.rmtree(self.tmp_dir) # Reset globals in cli # pylint: disable=protected-access - cli._parser = cli._set_by_cli.detector = None + cli._parser = cli.set_by_cli.detector = None def _call(self, args): "Run the cli with output streams and actual client mocked out" From 4bc8e9a44ac7b7dcd9a96eee0a34203850d6e1ea Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Wed, 23 Mar 2016 14:30:40 -0700 Subject: [PATCH 079/317] Improve mocking --- letsencrypt/tests/storage_test.py | 42 ++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 0d89156d3..9660e2688 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -493,7 +493,12 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertTrue(self.test_rc.should_autorenew()) mock_ocsp.return_value = False - def test_save_successor(self): + @mock.patch("letsencrypt.storage.relevant_values") + def test_save_successor(self, mock_rv): + # Mock relevant_values() to claim that all values are relevant here + # (to avoid instantiating parser) + mock_rv.side_effect = lambda x: x + for ver in xrange(1, 6): for kind in ALL_FOUR: where = getattr(self.test_rc, kind) @@ -557,24 +562,44 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10))) self.assertFalse(os.path.exists(temp_config_file)) - def test_relevant_values(self): + @mock.patch("letsencrypt.cli._parser") + def test_relevant_values(self, mock_parser): """Test that relevant_values() can reject an irrelevant value.""" from letsencrypt import storage + mock_parser.verb = "certonly" + mock_parser.args = ["--standalone"] + mock_action = mock.Mock(dest="rsa_key_size", default=2048) + mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"hello": "there"}), {}) - def test_relevant_values_default(self): + @mock.patch("letsencrypt.cli._parser") + def test_relevant_values_default(self, mock_parser): """Test that relevant_values() can reject a default value.""" from letsencrypt import storage + mock_parser.verb = "certonly" + mock_parser.args = ["--standalone"] + mock_action = mock.Mock(dest="rsa_key_size", default=2048) + mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"rsa_key_size": 2048}), {}) - def test_relevant_values_nondefault(self): + @mock.patch("letsencrypt.cli._parser") + def test_relevant_values_nondefault(self, mock_parser): """Test that relevant_values() can retain a non-default value.""" from letsencrypt import storage + mock_parser.verb = "certonly" + mock_parser.args = ["--standalone"] + mock_action = mock.Mock(dest="rsa_key_size", default=2048) + mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"rsa_key_size": 12}), {"rsa_key_size": 12}) - def test_new_lineage(self): + @mock.patch("letsencrypt.storage.relevant_values") + def test_new_lineage(self, mock_rv): """Test for new_lineage() class method.""" + # Mock relevant_values to say everything is relevant here (so we + # don't have to mock the parser to help it decide!) + mock_rv.side_effect = lambda x: x + from letsencrypt import storage result = storage.RenewableCert.new_lineage( "the-lineage.com", "cert", "privkey", "chain", self.cli_config) @@ -608,8 +633,13 @@ class RenewableCertTests(BaseRenewableCertTest): # TODO: Conceivably we could test that the renewal parameters actually # got saved - def test_new_lineage_nonexistent_dirs(self): + @mock.patch("letsencrypt.storage.relevant_values") + def test_new_lineage_nonexistent_dirs(self, mock_rv): """Test that directories can be created if they don't exist.""" + # Mock relevant_values to say everything is relevant here (so we + # don't have to mock the parser to help it decide!) + mock_rv.side_effect = lambda x: x + from letsencrypt import storage shutil.rmtree(self.cli_config.renewal_configs_dir) shutil.rmtree(self.cli_config.archive_dir) From 4f7c5b32e87809e650ff05d9a326295283fcad71 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Wed, 23 Mar 2016 14:57:01 -0700 Subject: [PATCH 080/317] Cleanup responding to protected-access problems --- letsencrypt/cli.py | 18 +++++++++--------- letsencrypt/plugins/common.py | 2 +- letsencrypt/storage.py | 6 +++--- letsencrypt/tests/cli_test.py | 4 ++-- letsencrypt/tests/storage_test.py | 3 +++ 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index fccebe02f..fa1e0090e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -271,20 +271,20 @@ def record_chosen_plugins(config, plugins, auth, inst): cn.installer = plugins.find_init(inst).name if inst else "none" -def _set_by_cli(var): +def set_by_cli(var): """ Return True if a particular config variable has been set by the user (CLI or config file) including if the user explicitly set it to the default. Returns False if the variable was assigned a default value. """ - detector = _set_by_cli.detector + detector = set_by_cli.detector if detector is None: # Setup on first run: `detector` is a weird version of config in which # the default value of every attribute is wrangled to be boolean-false plugins = plugins_disco.PluginsRegistry.find_all() # reconstructed_args == sys.argv[1:], or whatever was passed to main() reconstructed_args = _parser.args + [_parser.verb] - detector = _set_by_cli.detector = prepare_and_parse_args( + detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator auth, inst = cli_plugin_requests(detector) @@ -312,7 +312,7 @@ def _set_by_cli(var): else: return False # static housekeeping var -_set_by_cli.detector = None +set_by_cli.detector = None def _restore_required_config_elements(config, renewalparams): """Sets non-plugin specific values in config from renewalparams @@ -325,7 +325,7 @@ def _restore_required_config_elements(config, renewalparams): """ # string-valued items to add if they're present for config_item in STR_CONFIG_ITEMS: - if config_item in renewalparams and not _set_by_cli(config_item): + if config_item in renewalparams and not set_by_cli(config_item): value = renewalparams[config_item] # Unfortunately, we've lost type information from ConfigObj, # so we don't know if the original was NoneType or str! @@ -334,7 +334,7 @@ def _restore_required_config_elements(config, renewalparams): setattr(config.namespace, config_item, value) # int-valued items to add if they're present for config_item in INT_CONFIG_ITEMS: - if config_item in renewalparams and not _set_by_cli(config_item): + if config_item in renewalparams and not set_by_cli(config_item): config_value = renewalparams[config_item] # the default value for http01_port was None during private beta if config_item == "http01_port" and config_value == "None": @@ -378,7 +378,7 @@ def _restore_plugin_configs(config, renewalparams): plugin_prefixes.append(renewalparams["installer"]) for plugin_prefix in set(plugin_prefixes): for config_item, config_value in six.iteritems(renewalparams): - if config_item.startswith(plugin_prefix + "_") and not _set_by_cli(config_item): + if config_item.startswith(plugin_prefix + "_") and not set_by_cli(config_item): # Values None, True, and False need to be treated specially, # As they don't get parsed correctly based on type if config_value in ("None", "True", "False"): @@ -403,7 +403,7 @@ def _restore_webroot_config(config, renewalparams): if "webroot_map" in renewalparams: # if the user does anything that would create a new webroot map on the # CLI, don't use the old one - if not (_set_by_cli("webroot_map") or _set_by_cli("webroot_path")): + if not (set_by_cli("webroot_map") or set_by_cli("webroot_path")): setattr(config.namespace, "webroot_map", renewalparams["webroot_map"]) elif "webroot_path" in renewalparams: logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path") @@ -844,7 +844,7 @@ class HelpfulArgumentParser(object): def modify_arg_for_default_detection(self, *args, **kwargs): """ Adding an arg, but ensure that it has a default that evaluates to false, - so that _set_by_cli can tell if it was set. Only called if detect_defaults==True. + so that set_by_cli can tell if it was set. Only called if detect_defaults==True. :param list *args: the names of this argument flag :param dict **kwargs: various argparse settings for this argument diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 319692344..f6a2c3d76 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -52,7 +52,7 @@ class Plugin(object): NOTE: if you add argpase arguments such that users setting them can create a config entry that python's bool() would consider false (ie, the use might set the variable to "", [], 0, etc), please ensure that - cli._set_by_cli() works for your variable. + cli.set_by_cli() works for your variable. """ diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 5e5d70518..3807cb63d 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -152,7 +152,7 @@ def relevant_values(all_values): # Look through the CLI parser defaults and see if this option is # both present and equal to the specified value. If not, return # False. - for x in cli._parser.parser._actions: + for x in cli._parser.parser._actions: # pylint: disable=protected-access if x.dest == option: if x.default == value: return True @@ -164,10 +164,10 @@ def relevant_values(all_values): for option, value in all_values.iteritems(): # Try to find reasons to store this item in the # renewal config. It can be stored if it is relevant and - # (it is _set_by_cli() or flag_default() is different + # (it is set_by_cli() or flag_default() is different # from the value or flag_default() doesn't exist). if _relevant(option): - if (cli._set_by_cli(option) + if (cli.set_by_cli(option) or not _is_cli_default(option, value)): # or option not in constants.CLI_DEFAULTS # or constants.CLI_DEFAULTS[option] != value): diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 920bc2d99..74229cb6b 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -54,7 +54,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods shutil.rmtree(self.tmp_dir) # Reset globals in cli # pylint: disable=protected-access - cli._parser = cli._set_by_cli.detector = None + cli._parser = cli.set_by_cli.detector = None def _call(self, args): "Run the cli with output streams and actual client mocked out" @@ -660,7 +660,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args = ["renew", "--dry-run", "-tvv"] self._test_renewal_common(True, [], args=args, renew=True) - @mock.patch("letsencrypt.cli._set_by_cli") + @mock.patch("letsencrypt.cli.set_by_cli") def test_ancient_webroot_renewal_conf(self, mock_set_by_cli): mock_set_by_cli.return_value = False rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf') diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 9660e2688..0d007a831 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -565,6 +565,7 @@ class RenewableCertTests(BaseRenewableCertTest): @mock.patch("letsencrypt.cli._parser") def test_relevant_values(self, mock_parser): """Test that relevant_values() can reject an irrelevant value.""" + # pylint: disable=protected-access from letsencrypt import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] @@ -575,6 +576,7 @@ class RenewableCertTests(BaseRenewableCertTest): @mock.patch("letsencrypt.cli._parser") def test_relevant_values_default(self, mock_parser): """Test that relevant_values() can reject a default value.""" + # pylint: disable=protected-access from letsencrypt import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] @@ -585,6 +587,7 @@ class RenewableCertTests(BaseRenewableCertTest): @mock.patch("letsencrypt.cli._parser") def test_relevant_values_nondefault(self, mock_parser): """Test that relevant_values() can retain a non-default value.""" + # pylint: disable=protected-access from letsencrypt import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] From 9d70c4acfbc78fda0a2073f2d157a8e8bd9de84b Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 15:11:36 -0700 Subject: [PATCH 081/317] Add directory_select method to FileDisplay --- letsencrypt/display/util.py | 14 ++++++++++++++ letsencrypt/tests/display/util_test.py | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/letsencrypt/display/util.py b/letsencrypt/display/util.py index 05e280118..c94b0c921 100644 --- a/letsencrypt/display/util.py +++ b/letsencrypt/display/util.py @@ -7,6 +7,7 @@ import zope.interface from letsencrypt import interfaces from letsencrypt import errors +from letsencrypt.display import completer WIDTH = 72 HEIGHT = 20 @@ -339,6 +340,19 @@ class FileDisplay(object): else: return code, [] + def directory_select(self, message, **unused_kwargs): + """Display a directory selection screen. + + :param str message: prompt to give the user + + :returns: tuple of the form (`code`, `string`) where + `code` - int display exit code + `string` - input entered by the user + + """ + with completer.Completer(): + return self.input(message) + def _scrub_checklist_input(self, indices, tags): # pylint: disable=no-self-use """Validate input and transform indices to appropriate tags. diff --git a/letsencrypt/tests/display/util_test.py b/letsencrypt/tests/display/util_test.py index adec265dc..32783b3bd 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/letsencrypt/tests/display/util_test.py @@ -232,6 +232,15 @@ class FileOutputDisplayTest(unittest.TestCase): self.displayer._scrub_checklist_input(list_, TAGS)) self.assertEqual(set_tags, exp[i]) + @mock.patch("letsencrypt.display.util.FileDisplay.input") + def test_directory_select(self, mock_input): + message = "msg" + result = (display_util.OK, "/var/www/html",) + mock_input.return_value = result + + self.assertEqual(self.displayer.directory_select(message), result) + mock_input.assert_called_once_with(message) + def test_scrub_checklist_input_invalid(self): # pylint: disable=protected-access indices = [ From 294ea4d1a64be7be394854af177d35c60c2f1184 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 15:29:02 -0700 Subject: [PATCH 082/317] Add directory_select to NoninteractiveDisplay --- letsencrypt/display/util.py | 17 +++++++++++++++++ letsencrypt/tests/display/util_test.py | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/letsencrypt/display/util.py b/letsencrypt/display/util.py index c94b0c921..005e2ba9c 100644 --- a/letsencrypt/display/util.py +++ b/letsencrypt/display/util.py @@ -555,6 +555,23 @@ class NoninteractiveDisplay(object): else: return OK, default + def directory_select(self, message, default=None, cli_flag=None): + """Simulate prompting the user for a directory. + + This function returns default if it is not ``None``, otherwise, + an exception is raised. + + :param str message: prompt to give the user + :param default: default value to return (if one exists) + :param str cli_flag: option used to set this value with the CLI + + :returns: tuple of the form (`code`, `string`) where + `code` - int display exit code + `string` - input entered by the user + + """ + return self.input(message, default, cli_flag) + def separate_list_input(input_): """Separate a comma or space separated list. diff --git a/letsencrypt/tests/display/util_test.py b/letsencrypt/tests/display/util_test.py index 32783b3bd..bae0d582a 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/letsencrypt/tests/display/util_test.py @@ -335,6 +335,15 @@ class NoninteractiveDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.OK, d)) self.assertRaises(errors.MissingCommandlineFlag, self.displayer.checklist, "message", TAGS) + def test_directory_select(self): + default = "/var/www/html" + expected = (display_util.OK, default) + actual = self.displayer.directory_select("msg", default) + self.assertEqual(expected, actual) + + self.assertRaises( + errors.MissingCommandlineFlag, self.displayer.directory_select, "msg") + class SeparateListInputTest(unittest.TestCase): """Test Module functions.""" From 3031968ac5e148a857e057485f31dbbdbdbe0f39 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 23 Mar 2016 15:32:53 -0700 Subject: [PATCH 083/317] Add directory_select to IDisplay interface --- letsencrypt/interfaces.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 1921b1e54..188a4d9da 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -443,6 +443,20 @@ class IDisplay(zope.interface.Interface): """ + def directory_select(self, message, default=None, cli_flag=None): + """Display a directory selection screen. + + :param str message: prompt to give the user + :param default: the default value to return, if one exists, when + using the NoninteractiveDisplay + :param str cli_flag: option used to set this value with the CLI + + :returns: tuple of the form (`code`, `string`) where + `code` - int display exit code + `string` - input entered by the user + + """ + class IValidator(zope.interface.Interface): """Configuration validator.""" From a9faa3b4ba9b3353e3eb66dda48e303dc881312c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 23 Mar 2016 16:14:41 -0700 Subject: [PATCH 084/317] Shim renew() in main.py, keep the work in renewal.py --- letsencrypt/cli.py | 3 +-- letsencrypt/main.py | 11 ++++++++--- letsencrypt/{renew.py => renewal.py} | 4 ++-- letsencrypt/tests/cli_test.py | 6 +++--- 4 files changed, 14 insertions(+), 10 deletions(-) rename letsencrypt/{renew.py => renewal.py} (99%) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 933b51432..bc39c3a8d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -355,11 +355,10 @@ class HelpfulArgumentParser(object): def __init__(self, args, plugins, detect_defaults=False): from letsencrypt import main - from letsencrypt import renew self.VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, "config_changes": main.config_changes, "run": main.run, "install": main.install, "plugins": main.plugins_cmd, - "renew": renew.renew, "revoke": main.revoke, + "renew": main.renew, "revoke": main.revoke, "rollback": main.rollback, "everything": main.run} # List of topics for which additional help can be provided diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 90dfb456a..a3ebde64e 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -27,7 +27,7 @@ from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt import log from letsencrypt import reporter -from letsencrypt import renew +from letsencrypt import renewal from letsencrypt import storage from letsencrypt.display import util as display_util, ops as display_ops @@ -186,7 +186,7 @@ def _handle_identical_cert_request(config, cert): :rtype: tuple """ - if renew.should_renew(config, cert): + if renewal.should_renew(config, cert): return "renew", cert if config.reinstall: # Set with --reinstall, force an identical certificate to be @@ -263,7 +263,7 @@ def _find_duplicative_certs(config, domains): # Verify the directory is there le_util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid()) - for renewal_file in renew.renewal_conf_files(cli_config): + for renewal_file in renewal.renewal_conf_files(cli_config): try: candidate_lineage = storage.RenewableCert(renewal_file, cli_config) except (errors.CertStorageError, IOError): @@ -552,6 +552,11 @@ def obtain_cert(config, plugins, lineage=None): config.installer, "server; fullchain is", lineage.fullchain) _suggest_donation_if_appropriate(config, action) +def renew(config, unused_plugins): + """Renew previously-obtained certificates.""" + renewal.renew_all_lineages(config) + + def setup_log_file_handler(config, logfile, fmt): """Setup file debug logging.""" diff --git a/letsencrypt/renew.py b/letsencrypt/renewal.py similarity index 99% rename from letsencrypt/renew.py rename to letsencrypt/renewal.py index 9dc1ff4df..27546bec9 100644 --- a/letsencrypt/renew.py +++ b/letsencrypt/renewal.py @@ -236,8 +236,8 @@ def _renew_describe_results(config, renew_successes, renew_failures, print("** (The test certificates above have not been saved.)") -def renew(config, unused_plugins): - """Renew previously-obtained certificates.""" +def renew_all_lineages(config): + """Examine each lineage; renew if due and report results""" if config.domains != []: raise errors.Error("Currently, the renew verb is only capable of " diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 55a504f82..0c2bf5d54 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -23,7 +23,7 @@ from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import le_util from letsencrypt import main -from letsencrypt import renew +from letsencrypt import renewal from letsencrypt import storage from letsencrypt.plugins import disco @@ -666,7 +666,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods configuration.RenewerConfiguration(config)) renewalparams = lineage.configuration["renewalparams"] # pylint: disable=protected-access - renew._restore_webroot_config(config, renewalparams) + renewal._restore_webroot_config(config, renewalparams) self.assertEqual(config.webroot_path, ["/var/www/"]) def test_renew_verb_empty_config(self): @@ -745,7 +745,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_renew_reconstitute_error(self): # pylint: disable=protected-access - with mock.patch('letsencrypt.main.renew._reconstitute') as mock_reconstitute: + with mock.patch('letsencrypt.main.renewal._reconstitute') as mock_reconstitute: mock_reconstitute.side_effect = Exception self._test_renew_common(assert_oc_called=False, error_expected=True) From f3362d99784eb865c9cef7612566db8a73222fb0 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Wed, 23 Mar 2016 17:41:56 -0700 Subject: [PATCH 085/317] Disabling a protected access complaint --- letsencrypt/storage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 838ec868f..da5e5f665 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -148,7 +148,7 @@ def relevant_values(all_values): from letsencrypt import cli - def _is_cli_default(option, value): + def _is_cli_default(option, value): # pylint: disable=protected-access # Look through the CLI parser defaults and see if this option is # both present and equal to the specified value. If not, return # False. From a9e0b0f6ab07e5b5272d16768448e3e390333729 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Thu, 24 Mar 2016 12:02:11 -0700 Subject: [PATCH 086/317] Another try on protected access lint error --- letsencrypt/storage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index da5e5f665..59daa1a0d 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -148,10 +148,11 @@ def relevant_values(all_values): from letsencrypt import cli - def _is_cli_default(option, value): # pylint: disable=protected-access + def _is_cli_default(option, value): # Look through the CLI parser defaults and see if this option is # both present and equal to the specified value. If not, return # False. + # pylint: disable=protected-access for x in cli.helpful_parser.parser._actions: if x.dest == option: if x.default == value: From f3f665bf63f256c66ea1f3b1b03a665be6310d0e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 24 Mar 2016 12:35:52 -0700 Subject: [PATCH 087/317] s/none/None --- letsencrypt/cli.py | 4 ++-- letsencrypt/main.py | 2 +- letsencrypt/tests/testdata/sample-renewal-ancient.conf | 2 +- letsencrypt/tests/testdata/sample-renewal.conf | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index e10a531ab..1af4ef898 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -236,8 +236,8 @@ def choose_configurator_plugins(config, plugins, verb): def record_chosen_plugins(config, plugins, auth, inst): "Update the config entries to reflect the plugins we actually selected." cn = config.namespace - cn.authenticator = plugins.find_init(auth).name if auth else "none" - cn.installer = plugins.find_init(inst).name if inst else "none" + cn.authenticator = plugins.find_init(auth).name if auth else "None" + cn.installer = plugins.find_init(inst).name if inst else "None" def set_by_cli(var): diff --git a/letsencrypt/main.py b/letsencrypt/main.py index a3ebde64e..fc6540dcf 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -462,7 +462,7 @@ def config_changes(config, unused_plugins): def revoke(config, unused_plugins): # TODO: coop with renewal config """Revoke a previously obtained certificate.""" # For user-agent construction - config.namespace.installer = config.namespace.authenticator = "none" + config.namespace.installer = config.namespace.authenticator = "None" if config.key_path is not None: # revocation by cert key logger.debug("Revoking %s using cert key %s", config.cert_path[0], config.key_path[0]) diff --git a/letsencrypt/tests/testdata/sample-renewal-ancient.conf b/letsencrypt/tests/testdata/sample-renewal-ancient.conf index ff246ba7c..dd3075b8e 100644 --- a/letsencrypt/tests/testdata/sample-renewal-ancient.conf +++ b/letsencrypt/tests/testdata/sample-renewal-ancient.conf @@ -14,7 +14,7 @@ apache_dismod = a2dismod register_unsafely_without_email = False apache_handle_modules = True uir = None -installer = none +installer = None nginx_ctl = nginx config_dir = MAGICDIR text_mode = False diff --git a/letsencrypt/tests/testdata/sample-renewal.conf b/letsencrypt/tests/testdata/sample-renewal.conf index d6ebbd845..08032af86 100644 --- a/letsencrypt/tests/testdata/sample-renewal.conf +++ b/letsencrypt/tests/testdata/sample-renewal.conf @@ -14,7 +14,7 @@ apache_dismod = a2dismod register_unsafely_without_email = False apache_handle_modules = True uir = None -installer = none +installer = None nginx_ctl = nginx config_dir = MAGICDIR text_mode = False From 7b434c5a88aff2d6102680eb2b118e5a99db3620 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 24 Mar 2016 17:51:48 -0700 Subject: [PATCH 088/317] Address review comments --- letsencrypt/main.py | 8 ++++---- letsencrypt/plugins/selection.py | 4 +--- letsencrypt/tests/cli_test.py | 4 ++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 15c2de3ee..4e35a5227 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -32,7 +32,7 @@ from letsencrypt import storage from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco -from letsencrypt.plugins import selection as ps +from letsencrypt.plugins import selection as plug_sel logger = logging.getLogger(__name__) @@ -407,7 +407,7 @@ def install(config, plugins): # this function ... try: - installer, _ = ps.choose_configurator_plugins(config, plugins, "install") + installer, _ = plug_sel.choose_configurator_plugins(config, plugins, "install") except errors.PluginSelectionError as e: return e.message @@ -482,7 +482,7 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals # TODO: Make run as close to auth + install as possible # Possible difficulties: config.csr was hacked into auth try: - installer, authenticator = ps.choose_configurator_plugins(config, plugins, "run") + installer, authenticator = plug_sel.choose_configurator_plugins(config, plugins, "run") except errors.PluginSelectionError as e: return e.message @@ -512,7 +512,7 @@ def obtain_cert(config, plugins, lineage=None): # pylint: disable=too-many-locals try: # installers are used in auth mode to determine domain names - installer, authenticator = ps.choose_configurator_plugins(config, plugins, "certonly") + installer, authenticator = plug_sel.choose_configurator_plugins(config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise diff --git a/letsencrypt/plugins/selection.py b/letsencrypt/plugins/selection.py index 6f86075cd..057a09768 100644 --- a/letsencrypt/plugins/selection.py +++ b/letsencrypt/plugins/selection.py @@ -132,8 +132,6 @@ def choose_plugin(prepared, question): else: return None -logger = logging.getLogger(__name__) - noninstaller_plugins = ["webroot", "manual", "standalone"] def record_chosen_plugins(config, plugins, auth, inst): @@ -146,7 +144,7 @@ def record_chosen_plugins(config, plugins, auth, inst): def choose_configurator_plugins(config, plugins, verb): """ Figure out which configurator we're going to use, modifies - config.authenticator and config.istaller strings to reflect that choice if + config.authenticator and config.installer strings to reflect that choice if necessary. :raises errors.PluginSelectionError if there was a problem diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index b69716924..04b5a2f3c 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -203,8 +203,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(args.chain_path, os.path.abspath(chain)) self.assertEqual(args.fullchain_path, os.path.abspath(fullchain)) - @mock.patch('letsencrypt.main.ps.record_chosen_plugins') - @mock.patch('letsencrypt.main.ps.pick_installer') + @mock.patch('letsencrypt.main.plug_sel.record_chosen_plugins') + @mock.patch('letsencrypt.main.plug_sel.pick_installer') def test_installer_selection(self, mock_pick_installer, _rec): self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert', '--key-path', 'key', '--chain-path', 'chain']) From 2927746c0e8ea8163809d7b3ddd62ea375ab342d Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 25 Mar 2016 17:48:13 +0200 Subject: [PATCH 089/317] Added important check for IPv6 address and tests, improved readability --- letsencrypt/plugins/common.py | 5 ++++- letsencrypt/plugins/common_test.py | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index 707ed7e57..a9410d514 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -118,7 +118,7 @@ class Addr(object): port = '' if len(str_addr) > endIndex + 2 and str_addr[endIndex + 1] == ':': port = str_addr[endIndex + 2:] - return cls((host, port), True) + return cls((host, port), ipv6=True) else: tup = str_addr.partition(':') return cls((tup[0], tup[2])) @@ -173,6 +173,9 @@ class Addr(object): """Explode IPv6 address for comparison""" result = ['0', '0', '0', '0', '0', '0', '0', '0'] addr_list = addr.split(":") + if len(addr_list) > len(result): + # too long, truncate + addr_list = addr_list[0:len(result)] append_to_end = False for i in range(0, len(addr_list)): block = addr_list[i] diff --git a/letsencrypt/plugins/common_test.py b/letsencrypt/plugins/common_test.py index 9021e7e42..292248b90 100644 --- a/letsencrypt/plugins/common_test.py +++ b/letsencrypt/plugins/common_test.py @@ -84,6 +84,8 @@ class AddrTest(unittest.TestCase): self.addr4 = Addr.fromstring("[fe00::1]") self.addr5 = Addr.fromstring("[fe00::1]:*") self.addr6 = Addr.fromstring("[fe00::1]:80") + self.addr7 = Addr.fromstring("[fe00::1]:5") + self.addr8 = Addr.fromstring("[fe00:1:2:3:4:5:6:7:8:9]:8080") def test_fromstring(self): self.assertEqual(self.addr1.get_addr(), "192.168.1.1") @@ -102,6 +104,9 @@ class AddrTest(unittest.TestCase): "fe00:0:0:0:0:0:0:1") self.assertEqual(self.addr1.get_ipv6_exploded(), "") + self.assertEqual(self.addr7.get_port(), "5") + self.assertEqual(self.addr7.get_ipv6_exploded(), + "fe00:1:2:3:4:5:6:7") def test_str(self): self.assertEqual(str(self.addr1), "192.168.1.1") From f2fc79fc55e92abb2d19c7f1491af8f1aefe64cc Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 25 Mar 2016 19:12:48 +0200 Subject: [PATCH 090/317] Fixed test case --- letsencrypt/plugins/common_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/common_test.py b/letsencrypt/plugins/common_test.py index 292248b90..a4292151e 100644 --- a/letsencrypt/plugins/common_test.py +++ b/letsencrypt/plugins/common_test.py @@ -105,7 +105,7 @@ class AddrTest(unittest.TestCase): self.assertEqual(self.addr1.get_ipv6_exploded(), "") self.assertEqual(self.addr7.get_port(), "5") - self.assertEqual(self.addr7.get_ipv6_exploded(), + self.assertEqual(self.addr8.get_ipv6_exploded(), "fe00:1:2:3:4:5:6:7") def test_str(self): From 49b56d73c5b0717fb5de42b4dad4a3946a87d1da Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Fri, 25 Mar 2016 20:19:12 +0200 Subject: [PATCH 091/317] PEP8 fixes --- .../letsencrypt_apache/configurator.py | 15 ++++++++++----- .../letsencrypt_apache/constants.py | 8 ++++---- letsencrypt-apache/letsencrypt_apache/obj.py | 3 ++- .../letsencrypt_apache/tls_sni_01.py | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 3a679fa7e..b2c843251 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -345,7 +345,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def included_in_wildcard(self, names, target_name): """Helper function to see if alias is covered by wildcard""" target_name = target_name.split(".")[::-1] - wildcards = [domain.split(".")[1:] for domain in names if domain.startswith("*")] + wildcards = [domain.split(".")[1:] for domain in + names if domain.startswith("*")] for wildcard in wildcards: if len(wildcard) > len(target_name): continue @@ -545,7 +546,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): paths = self.aug.match( ("/files%s//*[label()=~regexp('%s')]" % (vhost_path, parser.case_i("VirtualHost")))) - paths = [path for path in paths if os.path.basename(path) == "VirtualHost"] + paths = [path for path in paths if + os.path.basename(path) == "VirtualHost"] for path in paths: new_vhost = self._create_vhost(path) realpath = os.path.realpath(new_vhost.filep) @@ -890,10 +892,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not vh_p: return vh_path = vh_p[0] - if (self.parser.find_dir("ServerName", target_name, start=vh_path, exclude=False) - or self.parser.find_dir("ServerAlias", target_name, start=vh_path, exclude=False)): + if (self.parser.find_dir("ServerName", target_name, + start=vh_path, exclude=False) or + self.parser.find_dir("ServerAlias", target_name, + start=vh_path, exclude=False)): return - if not self.parser.find_dir("ServerName", None, start=vh_path, exclude=False): + if not self.parser.find_dir("ServerName", None, + start=vh_path, exclude=False): self.parser.add_dir(vh_path, "ServerName", target_name) else: self.parser.add_dir(vh_path, "ServerAlias", target_name) diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/letsencrypt-apache/letsencrypt_apache/constants.py index 77f71a461..7537cae42 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/letsencrypt-apache/letsencrypt_apache/constants.py @@ -18,7 +18,7 @@ CLI_DEFAULTS_DEBIAN = dict( handle_sites=True, challenge_location="/etc/apache2", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "letsencrypt_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", @@ -35,7 +35,7 @@ CLI_DEFAULTS_CENTOS = dict( handle_sites=False, challenge_location="/etc/httpd/conf.d", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "centos-options-ssl-apache.conf") + "letsencrypt_apache", "centos-options-ssl-apache.conf") ) CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", @@ -52,7 +52,7 @@ CLI_DEFAULTS_GENTOO = dict( handle_sites=False, challenge_location="/etc/apache2/vhosts.d", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "letsencrypt_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS_DARWIN = dict( server_root="/etc/apache2", @@ -69,7 +69,7 @@ CLI_DEFAULTS_DARWIN = dict( handle_sites=False, challenge_location="/etc/apache2/other", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "letsencrypt_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, diff --git a/letsencrypt-apache/letsencrypt_apache/obj.py b/letsencrypt-apache/letsencrypt_apache/obj.py index 80a49b6a6..c05ee4bcc 100644 --- a/letsencrypt-apache/letsencrypt_apache/obj.py +++ b/letsencrypt-apache/letsencrypt_apache/obj.py @@ -208,7 +208,8 @@ class VirtualHost(object): # pylint: disable=too-few-public-methods # If equal and set is not empty... assume same server if self.name is not None or self.aliases: return True - # If we're looking for a generic vhost, don't return one with a ServerName + # If we're looking for a generic vhost, + # don't return one with a ServerName elif self.name: return False diff --git a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py index 3af61a7e7..9316427e5 100644 --- a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py +++ b/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py @@ -145,7 +145,7 @@ class ApacheTlsSni01(common.TLSSNI01): parser.case_i("Include"), self.challenge_conf)) == 0: # print "Including challenge virtual host(s)" logger.debug("Adding Include %s to %s", - self.challenge_conf, parser.get_aug_path(main_config)) + self.challenge_conf, parser.get_aug_path(main_config)) self.configurator.parser.add_dir( parser.get_aug_path(main_config), "Include", self.challenge_conf) From f520ca25565429141d44ed28e3b1fda705ea9682 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 25 Mar 2016 12:52:37 -0700 Subject: [PATCH 092/317] Address @schoen's review comments --- letsencrypt/display/completer.py | 18 +++++++++--------- letsencrypt/display/util.py | 6 ++++-- letsencrypt/interfaces.py | 4 +++- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/letsencrypt/display/completer.py b/letsencrypt/display/completer.py index 83dafa25d..fed476bb3 100644 --- a/letsencrypt/display/completer.py +++ b/letsencrypt/display/completer.py @@ -1,4 +1,4 @@ -"""Provides tab completion when prompting users for a path.""" +"""Provides Tab completion when prompting users for a path.""" import glob # readline module is not available on all systems try: @@ -8,9 +8,9 @@ except ImportError: class Completer(object): - """Provides tab completion when prompting users for a path. + """Provides Tab completion when prompting users for a path. - This class is meant to be used with readline to provide tab + This class is meant to be used with readline to provide Tab completion for users entering paths. The complete method can be passed to readline.set_completer directly, however, this function works best as a context manager. For example: @@ -18,14 +18,14 @@ class Completer(object): with Completer(): raw_input() - In this example, tab completion will be available during the call to + In this example, Tab completion will be available during the call to raw_input above, however, readline will be restored to its previous state when exiting the body of the with statement. """ def __init__(self): - self._completer = self._delims = self._iter = None + self._iter = self._original_completer = self._original_delims = None def complete(self, text, state): """Provides path completion for use with readline. @@ -43,8 +43,8 @@ class Completer(object): return next(self._iter, None) def __enter__(self): - self._completer = readline.get_completer() - self._delims = readline.get_completer_delims() + self._original_completer = readline.get_completer() + self._original_delims = readline.get_completer_delims() readline.set_completer(self.complete) readline.set_completer_delims(' \t\n;') @@ -57,5 +57,5 @@ class Completer(object): readline.parse_and_bind('tab: complete') def __exit__(self, unused_type, unused_value, unused_traceback): - readline.set_completer_delims(self._delims) - readline.set_completer(self._completer) + readline.set_completer_delims(self._original_delims) + readline.set_completer(self._original_completer) diff --git a/letsencrypt/display/util.py b/letsencrypt/display/util.py index 005e2ba9c..20c6be156 100644 --- a/letsencrypt/display/util.py +++ b/letsencrypt/display/util.py @@ -13,7 +13,7 @@ WIDTH = 72 HEIGHT = 20 DSELECT_HELP = ( - "Use the arrow keys or tab to move between window elements. Space can be " + "Use the arrow keys or Tab to move between window elements. Space can be " "used to complete the input path with the selected element in the " "directory window. Pressing enter will select the currently highlighted " "button.") @@ -559,7 +559,9 @@ class NoninteractiveDisplay(object): """Simulate prompting the user for a directory. This function returns default if it is not ``None``, otherwise, - an exception is raised. + an exception is raised explaining the problem. If cli_flag is + not ``None``, the error message will include the flag that can + be used to set this value with the CLI. :param str message: prompt to give the user :param default: default value to return (if one exists) diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 188a4d9da..2fba11869 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -449,7 +449,9 @@ class IDisplay(zope.interface.Interface): :param str message: prompt to give the user :param default: the default value to return, if one exists, when using the NoninteractiveDisplay - :param str cli_flag: option used to set this value with the CLI + :param str cli_flag: option used to set this value with the CLI, + if one exists, to be included in error messages given by + NoninteractiveDisplay :returns: tuple of the form (`code`, `string`) where `code` - int display exit code From fe078dfb954abd89dab04a807ea9374744ecb84c Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 25 Mar 2016 14:31:16 -0700 Subject: [PATCH 093/317] fixes #2712 --- Dockerfile | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71e217659..0e6700f90 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,10 +33,11 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages- # Dockerfile we make sure we cache as much as possible -COPY setup.py README.rst CHANGES.rst MANIFEST.in /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/letsencrypt/src/ -# all above files are necessary for setup.py, however, package source -# code directory has to be copied separately to a subdirectory... +# all above files are necessary for setup.py and venv setup, however, +# package source code directory has to be copied separately to a +# subdirectory... # https://docs.docker.com/reference/builder/#copy: "If is a # directory, the entire contents of the directory are copied, # including filesystem metadata. Note: The directory itself is not @@ -49,7 +50,11 @@ COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ -RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ +RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv + +# PATH is set now so pipstrap upgrades the correct v(env) +ENV PATH /opt/letsencrypt/venv/bin:$PATH +RUN /opt/letsencrypt/venv/bin/python /opt/letsencrypt/src/pipstrap.py && \ /opt/letsencrypt/venv/bin/pip install \ -e /opt/letsencrypt/src/acme \ -e /opt/letsencrypt/src \ @@ -61,6 +66,4 @@ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ # this might also help in debugging: you can "docker run --entrypoint # bash" and investigate, apply patches, etc. -ENV PATH /opt/letsencrypt/venv/bin:$PATH - ENTRYPOINT [ "letsencrypt" ] From c71b23754ce7fdfef65d90a8887a646f97d1f500 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 25 Mar 2016 14:47:12 -0700 Subject: [PATCH 094/317] What's a correct v? --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0e6700f90..ccbb07b95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,7 @@ COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv -# PATH is set now so pipstrap upgrades the correct v(env) +# PATH is set now so pipstrap upgrades the correct (v)env ENV PATH /opt/letsencrypt/venv/bin:$PATH RUN /opt/letsencrypt/venv/bin/python /opt/letsencrypt/src/pipstrap.py && \ /opt/letsencrypt/venv/bin/pip install \ From 5943dc7c3d33101b25b16ffc7b2d8fdfe90de1d4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 25 Mar 2016 20:37:12 -0700 Subject: [PATCH 095/317] Start implementing some renewal hook flags Also some refactoring: - split renewal out of _auth_from_domains into renewal.renew_cert - split main._csr_obtain_cert out of main.obtain_cert --- letsencrypt/cli.py | 18 ++++- letsencrypt/errors.py | 4 ++ letsencrypt/hooks.py | 96 +++++++++++++++++++++++++ letsencrypt/main.py | 129 +++++++++++++++------------------- letsencrypt/renewal.py | 47 +++++++++++++ letsencrypt/tests/cli_test.py | 4 +- 6 files changed, 222 insertions(+), 76 deletions(-) create mode 100644 letsencrypt/hooks.py diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 256e0c801..20fe60f23 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -692,7 +692,23 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): " used to create obtain or most recently successfully renew each" " certificate lineage. You can try it with `--dry-run` first. For" " more fine-grained control, you can renew individual lineages with" - " the `certonly` subcommand.") + " the `certonly` subcommand. Hooks are available to run commands " + " before and after renewal; see XXX for more information on these.") + + helpful.add( + "renew", "--pre-hook", + help="Command to be run in a shell before obtaining any certificates. Intended" + " primarily for renewal, where it can be used to temporarily shut down a" + " webserver that might conflict with the standalone plugin. This will " + " only be called if a certificate is actually to be obtained/renewed. ") + helpful.add( + "renew", "--post-hook", + help="Command to be run in a shell after attempting to obtain/renew " + " certificates. Can be used to deploy renewed certificates, or to restart" + " any servers that were stopped by --pre-hook.") + helpful.add( + "renew", "--renew-hook", + help="Command to be run in a shell once for each renewed certificate") helpful.add_deprecated_argument("--agree-dev-preview", 0) diff --git a/letsencrypt/errors.py b/letsencrypt/errors.py index b2b078f6a..532a3a545 100644 --- a/letsencrypt/errors.py +++ b/letsencrypt/errors.py @@ -25,6 +25,10 @@ class CertStorageError(Error): """Generic `.CertStorage` error.""" +class HookCommandNotFound(Error): + """Failed to find a hook command in the PATH.""" + + # Auth Handler Errors class AuthorizationError(Error): """Authorization error.""" diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py new file mode 100644 index 000000000..289dc0333 --- /dev/null +++ b/letsencrypt/hooks.py @@ -0,0 +1,96 @@ +"""Facilities for implementing hooks that call shell commands.""" +from __future__ import print_function + +import logging +import os + +from subprocess import Popen, PIPE + +from letsencrypt import errors + +logger = logging.getLogger(__name__) + +def validate_hooks(config): + """Check hook commands are executable.""" + _validate_hook(config.pre_hook) + _validate_hook(config.post_hook) + _validate_hook(config.renew_hook) + +def _prog(shell_cmd): + """Extract the program run by a shell command""" + cmd = _which(shell_cmd) + return os.path.basename(cmd) if cmd else None + +def _validate_hook(shell_cmd): + """Check that a command provided as a hook is plausibly executable. + + :raises .errors.HookCommandNotFound: if the command is not found + """ + cmd = shell_cmd.partition(" ")[0] + if not _prog(cmd): + path = os.environ["PATH"] + msg = "Unable to find command {0} in the PATH.\n(PATH is {1})".format( + cmd, path) + raise errors.HookCommandNotFound(msg) + + return True + +def pre_hook(config): + "Run pre-hook if it's defined and hasn't been run." + if config.pre_hook and not pre_hook.already: + logger.info("Running pre-hook command: %s", config.pre_hook) + _run_hook(config.pre_hook) + pre_hook.already = True + +pre_hook.already = False + +def post_hook(config, final=False): + """Run post hook if defined. + + If the verb is renew, we might have more certs to renew, so we wait until + we're called with final=True before actually doing anything. + """ + if config.post_hook: + if final or config.verb != "renew": + logger.info("Running post-hook command: %s", config.post_hook) + _run_hook(config.post_hook) + +def renew_hook(config, domains, lineage_path): + "Run post-renewal hook if defined." + if config.renew_hook: + os.environ["RENEWED_DOMAINS"] = " ".join(domains) + os.environ["RENEWED_LINEAGE"] = lineage_path + _run_hook(config.renew_hook) + +def _run_hook(shell_cmd): + """Run a hook command. + + :returns: stderr if there was any""" + + cmd = Popen(shell_cmd, shell=True, stdout=PIPE, stderr=PIPE) + _out, err = cmd.communicate() + if cmd.returncode != 0: + logger.error('Hook command "%s" returned error code %d', shell_cmd, cmd.returncode) + if err: + logger.error('Error output from %s:\n%s', _prog(shell_cmd), err) + +def _which(program): + """Test if program is in the path.""" + # Borrowed from: + # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python + # XXX May need more porting to handle .exe extensions on Windows + def _is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, _fname = os.path.split(program) + if fpath: + if _is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if _is_exe(exe_file): + return exe_file + + return None diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 0afccc85e..0df26c5be 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -8,7 +8,6 @@ import sys import time import traceback -import OpenSSL import zope.component from acme import jose @@ -23,6 +22,7 @@ from letsencrypt import colored_logging from letsencrypt import configuration from letsencrypt import constants from letsencrypt import errors +from letsencrypt import hooks from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt import log @@ -52,28 +52,6 @@ def _suggest_donation_if_appropriate(config, action): reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) -def _avoid_invalidating_lineage(config, lineage, original_server): - "Do not renew a valid cert with one from a staging server!" - def _is_staging(srv): - return srv == constants.STAGING_URI or "staging" in srv - - # Some lineages may have begun with --staging, but then had production certs - # added to them - latest_cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, - open(lineage.cert).read()) - # all our test certs are from happy hacker fake CA, though maybe one day - # we should test more methodically - now_valid = "fake" not in repr(latest_cert.get_issuer()).lower() - - if _is_staging(config.server): - if not _is_staging(original_server) or now_valid: - if not config.break_my_certs: - names = ", ".join(lineage.names()) - raise errors.Error( - "You've asked to renew/replace a seemingly valid certificate with " - "a test certificate (domains: {0}). We will not do that " - "unless you use the --break-my-certs flag!".format(names)) - def _report_successful_dry_run(config): reporter_util = zope.component.getUtility(interfaces.IReporter) @@ -82,6 +60,7 @@ def _report_successful_dry_run(config): reporter_util.HIGH_PRIORITY, on_crash=False) + def _auth_from_domains(le_client, config, domains, lineage=None): """Authenticate and enroll certificate.""" # Note: This can raise errors... caught above us though. This is now @@ -105,31 +84,18 @@ def _auth_from_domains(le_client, config, domains, lineage=None): # The lineage already exists; allow the caller to try installing # it without getting a new certificate at all. return lineage, "reinstall" - elif action == "renew": - original_server = lineage.configuration["renewalparams"]["server"] - _avoid_invalidating_lineage(config, lineage, original_server) - # TODO: schoen wishes to reuse key - discussion - # https://github.com/letsencrypt/letsencrypt/pull/777/files#r40498574 - new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) - # TODO: Check whether it worked! <- or make sure errors are thrown (jdk) - if config.dry_run: - logger.info("Dry run: skipping updating lineage at %s", - os.path.dirname(lineage.cert)) - else: - lineage.save_successor( - lineage.latest_common_version(), OpenSSL.crypto.dump_certificate( - OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped), - new_key.pem, crypto_util.dump_pyopenssl_chain(new_chain), - configuration.RenewerConfiguration(config.namespace)) - lineage.update_all_links_to(lineage.latest_common_version()) - # TODO: Check return value of save_successor - # TODO: Also update lineage renewal config with any relevant - # configuration values from this attempt? <- Absolutely (jdkasten) - elif action == "newcert": - # TREAT AS NEW REQUEST - lineage = le_client.obtain_and_enroll_certificate(domains) - if lineage is False: - raise errors.Error("Certificate could not be obtained") + + hooks.pre_hook(config) + try: + if action == "renew": + renewal.renew_cert(config, domains, le_client, lineage) + elif action == "newcert": + # TREAT AS NEW REQUEST + lineage = le_client.obtain_and_enroll_certificate(domains) + if lineage is False: + raise errors.Error("Certificate could not be obtained") + finally: + hooks.post_hook(config) if not config.dry_run and not config.verb == "renew": _report_new_cert(lineage.cert, lineage.fullchain) @@ -142,7 +108,8 @@ def _handle_subset_cert_request(config, domains, cert): :param storage.RenewableCert cert: - :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal + :returns: Tuple of (stringa action, cert_or_None) as per _treat_as_renewal + action can be: "newcert" | "renew" | "reinstall" :rtype: tuple """ @@ -183,7 +150,8 @@ def _handle_identical_cert_request(config, cert): :param storage.RenewableCert cert: - :returns: Tuple of (string, cert_or_None) as per _treat_as_renewal + :returns: Tuple of (string action, cert_or_None) as per _treat_as_renewal + action can be: "newcert" | "renew" | "reinstall" :rtype: tuple """ @@ -507,41 +475,53 @@ def run(config, plugins): # pylint: disable=too-many-branches,too-many-locals _suggest_donation_if_appropriate(config, action) +def _csr_obtain_cert(config, le_client): + """Obtain a cert using a user-supplied CSR + + This works differently in the CSR case (for now) because we don't + have the privkey, and therefore can't construct the files for a lineage. + So we just save the cert & chain to disk :/ + """ + csr, typ = config.actual_csr + certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) + if config.dry_run: + logger.info( + "Dry run: skipping saving certificate to %s", config.cert_path) + else: + cert_path, _, cert_fullchain = le_client.save_certificate( + certr, chain, config.cert_path, config.chain_path, config.fullchain_path) + _report_new_cert(cert_path, cert_fullchain) + + def obtain_cert(config, plugins, lineage=None): - """Implements "certonly": authenticate & obtain cert, but do not install it.""" - # pylint: disable=too-many-locals + """Authenticate & obtain cert, but do not install it. + + This implements the 'certonly' subcommand, and is also called from within the + 'renew' command.""" + + # SETUP: Select plugins and construct a client instance try: # installers are used in auth mode to determine domain names - installer, authenticator = plug_sel.choose_configurator_plugins(config, plugins, "certonly") + installer, auth = plug_sel.choose_configurator_plugins(config, plugins, "certonly") except errors.PluginSelectionError as e: logger.info("Could not choose appropriate plugin: %s", e) raise + le_client = _init_le_client(config, auth, installer) - # TODO: Handle errors from _init_le_client? - le_client = _init_le_client(config, authenticator, installer) - - action = "newcert" - # This is a special case; cert and chain are simply saved - if config.csr is not None: - assert lineage is None, "Did not expect a CSR with a RenewableCert" - csr, typ = config.actual_csr - certr, chain = le_client.obtain_certificate_from_csr(config.domains, csr, typ) - if config.dry_run: - logger.info( - "Dry run: skipping saving certificate to %s", config.cert_path) - else: - cert_path, _, cert_fullchain = le_client.save_certificate( - certr, chain, config.cert_path, config.chain_path, config.fullchain_path) - _report_new_cert(cert_path, cert_fullchain) - else: + # SHOWTIME: Possibly obtain/renew a cert, and set action to renew | newcert | reinstall + if config.csr is None: # the common case domains = _find_domains(config, installer) _, action = _auth_from_domains(le_client, config, domains, lineage) + else: + assert lineage is None, "Did not expect a CSR with a RenewableCert" + _csr_obtain_cert(config, le_client) + action = "newcert" + # POSTPRODUCTION: Cleanup, deployment & reporting if config.dry_run: _report_successful_dry_run(config) elif config.verb == "renew": if installer is None: - # Tell the user that the server was not restarted. print("new certificate deployed without reload, fullchain is", lineage.fullchain) else: @@ -553,10 +533,13 @@ def obtain_cert(config, plugins, lineage=None): config.installer, "server; fullchain is", lineage.fullchain) _suggest_donation_if_appropriate(config, action) + def renew(config, unused_plugins): """Renew previously-obtained certificates.""" - renewal.renew_all_lineages(config) - + try: + renewal.renew_all_lineages(config) + finally: + hooks.post_hook(config, final=True) def setup_log_file_handler(config, logfile, fmt): diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 27546bec9..7a9b18867 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -9,8 +9,13 @@ import traceback import six import zope.component +import OpenSSL + from letsencrypt import configuration from letsencrypt import cli +from letsencrypt import constants + +from letsencrypt import crypto_util from letsencrypt import errors from letsencrypt import storage from letsencrypt.plugins import disco as plugins_disco @@ -199,6 +204,48 @@ def should_renew(config, lineage): return False +def _avoid_invalidating_lineage(config, lineage, original_server): + "Do not renew a valid cert with one from a staging server!" + def _is_staging(srv): + return srv == constants.STAGING_URI or "staging" in srv + + # Some lineages may have begun with --staging, but then had production certs + # added to them + latest_cert = OpenSSL.crypto.load_certificate( + OpenSSL.crypto.FILETYPE_PEM, open(lineage.cert).read()) + # all our test certs are from happy hacker fake CA, though maybe one day + # we should test more methodically + now_valid = "fake" not in repr(latest_cert.get_issuer()).lower() + + if _is_staging(config.server): + if not _is_staging(original_server) or now_valid: + if not config.break_my_certs: + names = ", ".join(lineage.names()) + raise errors.Error( + "You've asked to renew/replace a seemingly valid certificate with " + "a test certificate (domains: {0}). We will not do that " + "unless you use the --break-my-certs flag!".format(names)) + + +def renew_cert(config, domains, le_client, lineage): + "Renew a certificate lineage." + original_server = lineage.configuration["renewalparams"]["server"] + _avoid_invalidating_lineage(config, lineage, original_server) + new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) + if config.dry_run: + logger.info("Dry run: skipping updating lineage at %s", + os.path.dirname(lineage.cert)) + else: + prior_version = lineage.latest_common_version() + new_cert = OpenSSL.crypto.dump_certificate( + OpenSSL.crypto.FILETYPE_PEM, new_certr.body.wrapped) + new_chain = crypto_util.dump_pyopenssl_chain(new_chain) + renewal_conf = configuration.RenewerConfiguration(config.namespace) + lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, renewal_conf) + lineage.update_all_links_to(lineage.latest_common_version()) + # TODO: Check return value of save_successor + + def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): def _status(msgs, category): diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 04b5a2f3c..de8b0c7e8 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -579,11 +579,11 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_init.return_value = mock_client get_utility_path = 'letsencrypt.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: - with mock.patch('letsencrypt.main.OpenSSL') as mock_ssl: + with mock.patch('letsencrypt.main.renewal.OpenSSL') as mock_ssl: mock_latest = mock.MagicMock() mock_latest.get_issuer.return_value = "Fake fake" mock_ssl.crypto.load_certificate.return_value = mock_latest - with mock.patch('letsencrypt.main.crypto_util'): + with mock.patch('letsencrypt.main.renewal.crypto_util'): if not args: args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly'] if extra_args: From 1205b404112c7768516630e94e02a010a5eaf1ba Mon Sep 17 00:00:00 2001 From: Gregor Dschung Date: Sun, 27 Mar 2016 10:54:41 +0200 Subject: [PATCH 096/317] Revise the concatenation order for fullchain.pem --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 463680389..66c5907ae 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -313,7 +313,7 @@ The following files are available: ``fullchain.pem`` All certificates, **including** server certificate. This is - concatenation of ``chain.pem`` and ``cert.pem``. + concatenation of ``cert.pem`` and ``chain.pem``. This is what Apache >= 2.4.8 needs for `SSLCertificateFile `_, From ad5a08f5fc6e3cc6f4d9b73cc83e4e1e8292b071 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Mar 2016 17:58:53 -0700 Subject: [PATCH 097/317] Actually run the renew hook --- letsencrypt/hooks.py | 2 +- letsencrypt/renewal.py | 1 + letsencrypt/storage.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index 289dc0333..b7fd8b4e9 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -67,7 +67,7 @@ def _run_hook(shell_cmd): :returns: stderr if there was any""" - cmd = Popen(shell_cmd, shell=True, stdout=PIPE, stderr=PIPE) + cmd = Popen(shell_cmd, shell=True, stdout=PIPE, stderr=PIPE, stdin=PIPE) _out, err = cmd.communicate() if cmd.returncode != 0: logger.error('Hook command "%s" returned error code %d', shell_cmd, cmd.returncode) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 7a9b18867..b42b62caa 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -243,6 +243,7 @@ def renew_cert(config, domains, le_client, lineage): renewal_conf = configuration.RenewerConfiguration(config.namespace) lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, renewal_conf) lineage.update_all_links_to(lineage.latest_common_version()) + hooks.renew_hook(config, domains, lineage.live_dir) # TODO: Check return value of save_successor diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 59daa1a0d..89f7d0f70 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -252,6 +252,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes self.privkey = self.configuration["privkey"] self.chain = self.configuration["chain"] self.fullchain = self.configuration["fullchain"] + self.live_dir = os.path.dirname(self.cert) self._fix_symlinks() self._check_symlinks() From 87aa1bd14012229050ec062ce38c9b81ec4ff4ff Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Mar 2016 18:15:50 -0700 Subject: [PATCH 098/317] lint --- letsencrypt/hooks.py | 9 ++++++--- letsencrypt/renewal.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index b7fd8b4e9..f3561628c 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -58,9 +58,12 @@ def post_hook(config, final=False): def renew_hook(config, domains, lineage_path): "Run post-renewal hook if defined." if config.renew_hook: - os.environ["RENEWED_DOMAINS"] = " ".join(domains) - os.environ["RENEWED_LINEAGE"] = lineage_path - _run_hook(config.renew_hook) + if not config.dry_run: + os.environ["RENEWED_DOMAINS"] = " ".join(domains) + os.environ["RENEWED_LINEAGE"] = lineage_path + _run_hook(config.renew_hook) + else: + print("Dry run: skipping renewal hook command: {0}".format(config.renew_hook)) def _run_hook(shell_cmd): """Run a hook command. diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index b42b62caa..3b0dd3ea0 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -17,6 +17,7 @@ from letsencrypt import constants from letsencrypt import crypto_util from letsencrypt import errors +from letsencrypt import hooks from letsencrypt import storage from letsencrypt.plugins import disco as plugins_disco From 3265660478e15c11b8eb3e82000707907d2a13db Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Mar 2016 18:33:57 -0700 Subject: [PATCH 099/317] Dry run testable --- letsencrypt/cli.py | 5 ++++- letsencrypt/renewal.py | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 20fe60f23..41c6ff186 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -708,7 +708,10 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): " any servers that were stopped by --pre-hook.") helpful.add( "renew", "--renew-hook", - help="Command to be run in a shell once for each renewed certificate") + help="Command to be run in a shell once for each renewed certificate." + "For this command, the shell variable $RENEWED_LINEAGE will point to the" + "config live subdirectory containing the new certs and keys; the shell variable " + "$RENEWED_DOMAINS will conatain a space-delimited list of renewed cert domains") helpful.add_deprecated_argument("--agree-dev-preview", 0) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 3b0dd3ea0..8a172098b 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -244,7 +244,8 @@ def renew_cert(config, domains, le_client, lineage): renewal_conf = configuration.RenewerConfiguration(config.namespace) lineage.save_successor(prior_version, new_cert, new_key.pem, new_chain, renewal_conf) lineage.update_all_links_to(lineage.latest_common_version()) - hooks.renew_hook(config, domains, lineage.live_dir) + + hooks.renew_hook(config, domains, lineage.live_dir) # TODO: Check return value of save_successor From 8b8319355df4cf57760d93d8461270e6ab6fa53e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Mar 2016 18:45:14 -0700 Subject: [PATCH 100/317] Actually validate hooks --- letsencrypt/cli.py | 3 +++ letsencrypt/hooks.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 41c6ff186..cc25e3d9e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -18,6 +18,7 @@ import letsencrypt from letsencrypt import constants from letsencrypt import crypto_util from letsencrypt import errors +from letsencrypt import hooks from letsencrypt import interfaces from letsencrypt import le_util @@ -306,6 +307,8 @@ class HelpfulArgumentParser(object): if self.detect_defaults: # plumbing parsed_args.store_false_vars = self.store_false_vars + hooks.validate_hooks(parsed_args) + return parsed_args def handle_csr(self, parsed_args): diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index f3561628c..b734a89d3 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -27,7 +27,7 @@ def _validate_hook(shell_cmd): :raises .errors.HookCommandNotFound: if the command is not found """ cmd = shell_cmd.partition(" ")[0] - if not _prog(cmd): + if shell_cmd and not _prog(cmd): path = os.environ["PATH"] msg = "Unable to find command {0} in the PATH.\n(PATH is {1})".format( cmd, path) From ffefac466a3da6c1c3d1f94b1c98775fdbeb45b5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 29 Mar 2016 18:46:51 -0700 Subject: [PATCH 101/317] Make hook errors more helpful --- letsencrypt/hooks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index b734a89d3..4a3fabf37 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -12,16 +12,16 @@ logger = logging.getLogger(__name__) def validate_hooks(config): """Check hook commands are executable.""" - _validate_hook(config.pre_hook) - _validate_hook(config.post_hook) - _validate_hook(config.renew_hook) + _validate_hook(config.pre_hook, "pre") + _validate_hook(config.post_hook, "post") + _validate_hook(config.renew_hook, "renew") def _prog(shell_cmd): """Extract the program run by a shell command""" cmd = _which(shell_cmd) return os.path.basename(cmd) if cmd else None -def _validate_hook(shell_cmd): +def _validate_hook(shell_cmd, hook_name): """Check that a command provided as a hook is plausibly executable. :raises .errors.HookCommandNotFound: if the command is not found @@ -29,8 +29,8 @@ def _validate_hook(shell_cmd): cmd = shell_cmd.partition(" ")[0] if shell_cmd and not _prog(cmd): path = os.environ["PATH"] - msg = "Unable to find command {0} in the PATH.\n(PATH is {1})".format( - cmd, path) + msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format( + cmd, path, hook_name) raise errors.HookCommandNotFound(msg) return True From c11af67f2a575f30a3d88949fb3c9572d7e33768 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 13:15:54 -0700 Subject: [PATCH 102/317] Some unit tests for hooks.py --- letsencrypt/hooks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index 4a3fabf37..f6450022f 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -77,13 +77,14 @@ def _run_hook(shell_cmd): if err: logger.error('Error output from %s:\n%s', _prog(shell_cmd), err) +def _is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + def _which(program): """Test if program is in the path.""" # Borrowed from: # https://stackoverflow.com/questions/377017/test-if-executable-exists-in-python # XXX May need more porting to handle .exe extensions on Windows - def _is_exe(fpath): - return os.path.isfile(fpath) and os.access(fpath, os.X_OK) fpath, _fname = os.path.split(program) if fpath: From c98bdd6988ac50c995b37256b077fb8aacec357d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 13:33:55 -0700 Subject: [PATCH 103/317] Don't try to test empty hooks --- letsencrypt/hooks.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index f6450022f..ed491ab72 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -26,14 +26,13 @@ def _validate_hook(shell_cmd, hook_name): :raises .errors.HookCommandNotFound: if the command is not found """ - cmd = shell_cmd.partition(" ")[0] - if shell_cmd and not _prog(cmd): - path = os.environ["PATH"] - msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format( - cmd, path, hook_name) - raise errors.HookCommandNotFound(msg) - - return True + if shell_cmd: + cmd = shell_cmd.partition(" ")[0] + if not _prog(cmd): + path = os.environ["PATH"] + msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format( + cmd, path, hook_name) + raise errors.HookCommandNotFound(msg) def pre_hook(config): "Run pre-hook if it's defined and hasn't been run." From 509600dec146fee99e2fa6a7f33de3cfefdb3b3e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 13:56:10 -0700 Subject: [PATCH 104/317] Address review comments --- letsencrypt/cli.py | 2 +- letsencrypt/main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index cc25e3d9e..1091a804b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -711,7 +711,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): " any servers that were stopped by --pre-hook.") helpful.add( "renew", "--renew-hook", - help="Command to be run in a shell once for each renewed certificate." + help="Command to be run in a shell once for each successfully renewed certificate." "For this command, the shell variable $RENEWED_LINEAGE will point to the" "config live subdirectory containing the new certs and keys; the shell variable " "$RENEWED_DOMAINS will conatain a space-delimited list of renewed cert domains") diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 0df26c5be..d2962ba87 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -108,7 +108,7 @@ def _handle_subset_cert_request(config, domains, cert): :param storage.RenewableCert cert: - :returns: Tuple of (stringa action, cert_or_None) as per _treat_as_renewal + :returns: Tuple of (str action, cert_or_None) as per _treat_as_renewal action can be: "newcert" | "renew" | "reinstall" :rtype: tuple @@ -150,7 +150,7 @@ def _handle_identical_cert_request(config, cert): :param storage.RenewableCert cert: - :returns: Tuple of (string action, cert_or_None) as per _treat_as_renewal + :returns: Tuple of (str action, cert_or_None) as per _treat_as_renewal action can be: "newcert" | "renew" | "reinstall" :rtype: tuple From af1b13684659ddeaad9f95a91db32aa7a09d3137 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 13:58:26 -0700 Subject: [PATCH 105/317] Ooops, actually add hook_test.py to the repo! --- letsencrypt/tests/hook_test.py | 106 +++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 letsencrypt/tests/hook_test.py diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py new file mode 100644 index 000000000..c491cd58b --- /dev/null +++ b/letsencrypt/tests/hook_test.py @@ -0,0 +1,106 @@ +"""Tests for hooks.py""" + +import os +import unittest + +import mock + +from letsencrypt import errors +from letsencrypt import hooks + +from letsencrypt.tests import test_util + +class HookTest(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + @mock.patch('letsencrypt.hooks._prog') + def test_validate_hooks(self, mock_prog): + config = mock.MagicMock(pre_hook="", post_hook="ls -lR", renew_hook="uptime") + hooks.validate_hooks(config) + self.assertEqual(mock_prog.call_count, 2) + self.assertEqual(mock_prog.call_args_list[1][0][0], 'uptime') + self.assertEqual(mock_prog.call_args_list[0][0][0], 'ls') + mock_prog.return_value = None + config = mock.MagicMock(pre_hook="explodinator", post_hook="", renew_hook="") + self.assertRaises(errors.HookCommandNotFound, hooks.validate_hooks, config) + + @mock.patch('letsencrypt.hooks._is_exe') + def test_which(self, mock_is_exe): + mock_is_exe.return_value = True + self.assertEqual(hooks._which("/path/to/something"), "/path/to/something") + + with mock.patch.dict('os.environ', {"PATH": "/floop:/fleep"}): + mock_is_exe.return_value = True + self.assertEqual(hooks._which("pingify"), "/floop/pingify") + mock_is_exe.return_value = False + self.assertEqual(hooks._which("pingify"), None) + self.assertEqual(hooks._which("/path/to/something"), None) + + @mock.patch('letsencrypt.hooks._which') + def test_prog(self, mockwhich): + mockwhich.return_value = "/very/very/funky" + self.assertEqual(hooks._prog("funky"), "funky") + mockwhich.return_value = None + self.assertEqual(hooks._prog("funky"), None) + + def _test_a_hook(self, config, hook_function, calls_expected): + with mock.patch('letsencrypt.hooks.logger'): + with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook: + hook_function(config) + hook_function(config) + self.assertEqual(mock_run_hook.call_count, calls_expected) + + def test_pre_hook(self): + config = mock.MagicMock(pre_hook="true") + self._test_a_hook(config, hooks.pre_hook, 1) + config = mock.MagicMock(pre_hook="") + self._test_a_hook(config, hooks.pre_hook, 0) + + def test_post_hook(self): + config = mock.MagicMock(post_hook="true", verb="splonk") + self._test_a_hook(config, hooks.post_hook, 2) + config = mock.MagicMock(post_hook="true", verb="renew") + self._test_a_hook(config, hooks.post_hook, 0) + + def test_renew_hook(self): + with mock.patch.dict('os.environ', {}): + domains = ["a", "b"] + lineage = "thing" + rhook = lambda x: hooks.renew_hook(x, domains, lineage) + + config = mock.MagicMock(renew_hook="true", dry_run=False) + self._test_a_hook(config, rhook, 2) + self.assertEqual(os.environ["RENEWED_DOMAINS"], "a b") + self.assertEqual(os.environ["RENEWED_LINEAGE"], "thing") + + with mock.patch("letsencrypt.hooks.print") as mock_print: + config = mock.MagicMock(renew_hook="true", dry_run=True) + self._test_a_hook(config, rhook, 0) + self.assertEqual(mock_print.call_count, 2) + + @mock.patch('letsencrypt.hooks.logger.error') + @mock.patch('letsencrypt.hooks.Popen') + def test_run_hook(self, mock_popen, mock_error): + + mock_cmd = mock.MagicMock() + mock_cmd.returncode = 1 + mock_cmd.communicate.return_value = ("", "") + mock_popen.return_value = mock_cmd + hooks._run_hook("ls") + self.assertEqual(mock_error.call_count, 1) + + + + + + + +if __name__ == '__main__': + unittest.main() # pragma: no cover + + + From db5c4f9f91be7b5e18f40415dd334a619ae77c69 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 13:59:12 -0700 Subject: [PATCH 106/317] Fix whitespace --- letsencrypt/tests/hook_test.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py index c491cd58b..671ea3fc8 100644 --- a/letsencrypt/tests/hook_test.py +++ b/letsencrypt/tests/hook_test.py @@ -85,18 +85,15 @@ class HookTest(unittest.TestCase): @mock.patch('letsencrypt.hooks.logger.error') @mock.patch('letsencrypt.hooks.Popen') def test_run_hook(self, mock_popen, mock_error): - mock_cmd = mock.MagicMock() mock_cmd.returncode = 1 mock_cmd.communicate.return_value = ("", "") mock_popen.return_value = mock_cmd hooks._run_hook("ls") self.assertEqual(mock_error.call_count, 1) - - - - - + mock_cmd.communicate.return_value = ("", "thing") + hooks._run_hook("ls") + self.assertEqual(mock_error.call_count, 2) if __name__ == '__main__': From 285f3b5536bf2a2af6a42d09c74eaaf95faa3a32 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 15:38:38 -0700 Subject: [PATCH 107/317] lint --- letsencrypt/tests/hook_test.py | 14 +++++--------- letsencrypt/tests/storage_test.py | 1 + 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py index 671ea3fc8..ffd97aa47 100644 --- a/letsencrypt/tests/hook_test.py +++ b/letsencrypt/tests/hook_test.py @@ -1,4 +1,5 @@ """Tests for hooks.py""" +# pylint: disable=protected-access import os import unittest @@ -8,8 +9,6 @@ import mock from letsencrypt import errors from letsencrypt import hooks -from letsencrypt.tests import test_util - class HookTest(unittest.TestCase): def setUp(self): pass @@ -30,11 +29,11 @@ class HookTest(unittest.TestCase): @mock.patch('letsencrypt.hooks._is_exe') def test_which(self, mock_is_exe): - mock_is_exe.return_value = True + mock_is_exe.return_value = True self.assertEqual(hooks._which("/path/to/something"), "/path/to/something") with mock.patch.dict('os.environ', {"PATH": "/floop:/fleep"}): - mock_is_exe.return_value = True + mock_is_exe.return_value = True self.assertEqual(hooks._which("pingify"), "/floop/pingify") mock_is_exe.return_value = False self.assertEqual(hooks._which("pingify"), None) @@ -42,7 +41,7 @@ class HookTest(unittest.TestCase): @mock.patch('letsencrypt.hooks._which') def test_prog(self, mockwhich): - mockwhich.return_value = "/very/very/funky" + mockwhich.return_value = "/very/very/funky" self.assertEqual(hooks._prog("funky"), "funky") mockwhich.return_value = None self.assertEqual(hooks._prog("funky"), None) @@ -53,7 +52,7 @@ class HookTest(unittest.TestCase): hook_function(config) hook_function(config) self.assertEqual(mock_run_hook.call_count, calls_expected) - + def test_pre_hook(self): config = mock.MagicMock(pre_hook="true") self._test_a_hook(config, hooks.pre_hook, 1) @@ -98,6 +97,3 @@ class HookTest(unittest.TestCase): if __name__ == '__main__': unittest.main() # pragma: no cover - - - diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 49b4f0821..7e862146d 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -1,4 +1,5 @@ """Tests for letsencrypt.storage.""" +# pylint disable=protected-access import datetime import os import shutil From b13ce26eb3999924987714dcccba7f01b0540bfa Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Wed, 30 Mar 2016 16:59:13 -0700 Subject: [PATCH 108/317] Basic attempt at fixing #2368 Changing the method of updating and rewriting renewal config files to use the same ConfigObj instance rather than a new one, and change individual renewalparams items individually rather than replacing the entire renewalparams dict with a new dict (which may also cause a loss of associated comment data). --- letsencrypt/storage.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/letsencrypt/storage.py b/letsencrypt/storage.py index 59daa1a0d..5e0e2aa06 100644 --- a/letsencrypt/storage.py +++ b/letsencrypt/storage.py @@ -50,10 +50,11 @@ def add_time_interval(base_time, interval, textparser=parsedatetime.Calendar()): return textparser.parseDT(interval, base_time, tzinfo=tzinfo)[0] -def write_renewal_config(filename, target, relevant_data): +def write_renewal_config(o_filename, n_filename, target, relevant_data): """Writes a renewal config file with the specified name and values. - :param str filename: Absolute path to the config file + :param str o_filename: Absolute path to the previous version of config file + :param str n_filename: Absolute path to the new destination of config file :param dict target: Maps ALL_FOUR to their symlink paths :param dict relevant_data: Renewal configuration options to save @@ -61,21 +62,27 @@ def write_renewal_config(filename, target, relevant_data): :rtype: configobj.ConfigObj """ - # create_empty creates a new config file if filename does not exist - config = configobj.ConfigObj(filename, create_empty=True) + config = configobj.ConfigObj(o_filename) for kind in ALL_FOUR: config[kind] = target[kind] - if relevant_data: - config["renewalparams"] = relevant_data + if "renewalparams" not in config: + config["renewalparams"] = {} config.comments["renewalparams"] = ["", "Options used in " "the renewal process"] + config["renewalparams"].update(relevant_data) + + for k in config["renewalparams"].keys(): + if k not in relevant_data: + del config["renewalparams"][k] + # TODO: add human-readable comments explaining other available # parameters - logger.debug("Writing new config %s.", filename) - config.write() + logger.debug("Writing new config %s.", n_filename) + with open(n_filename, "w") as f: + config.write(outfile=f) return config @@ -101,7 +108,7 @@ def update_configuration(lineagename, target, cli_config): # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) - write_renewal_config(temp_filename, target, values) + write_renewal_config(config_filename, temp_filename, target, values) os.rename(temp_filename, config_filename) return configobj.ConfigObj(config_filename) @@ -798,7 +805,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes # Save only the config items that are relevant to renewal values = relevant_values(vars(cli_config.namespace)) - new_config = write_renewal_config(config_filename, target, values) + new_config = write_renewal_config(config_filename, config_filename, target, values) return cls(new_config.filename, cli_config) def save_successor(self, prior_version, new_cert, From 526aa7d5ae783f8fa9743ea2b9359c1033ad20a3 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 17:41:59 -0700 Subject: [PATCH 109/317] Fix _run_hook test --- letsencrypt/tests/hook_test.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py index ffd97aa47..45025b054 100644 --- a/letsencrypt/tests/hook_test.py +++ b/letsencrypt/tests/hook_test.py @@ -81,18 +81,19 @@ class HookTest(unittest.TestCase): self._test_a_hook(config, rhook, 0) self.assertEqual(mock_print.call_count, 2) - @mock.patch('letsencrypt.hooks.logger.error') @mock.patch('letsencrypt.hooks.Popen') - def test_run_hook(self, mock_popen, mock_error): - mock_cmd = mock.MagicMock() - mock_cmd.returncode = 1 - mock_cmd.communicate.return_value = ("", "") - mock_popen.return_value = mock_cmd - hooks._run_hook("ls") - self.assertEqual(mock_error.call_count, 1) - mock_cmd.communicate.return_value = ("", "thing") - hooks._run_hook("ls") - self.assertEqual(mock_error.call_count, 2) + def test_run_hook(self, mock_popen): + with mock.patch('letsencrypt.hooks.logger.error') as mock_error: + mock_cmd = mock.MagicMock() + mock_cmd.returncode = 1 + mock_cmd.communicate.return_value = ("", "") + mock_popen.return_value = mock_cmd + hooks._run_hook("ls") + self.assertEqual(mock_error.call_count, 1) + with mock.patch('letsencrypt.hooks.logger.error') as mock_error: + mock_cmd.communicate.return_value = ("", "thing") + hooks._run_hook("ls") + self.assertEqual(mock_error.call_count, 2) if __name__ == '__main__': From 8f8d80e7d1f1a35b7db1c36b81c14eed907740a9 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 30 Mar 2016 17:50:22 -0700 Subject: [PATCH 110/317] Don't strip " from PATH entries There shouldn't be any, but if they're there perhaps they're there for a reason. --- letsencrypt/hooks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index ed491ab72..6a0997708 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -91,7 +91,6 @@ def _which(program): return program else: for path in os.environ["PATH"].split(os.pathsep): - path = path.strip('"') exe_file = os.path.join(path, program) if _is_exe(exe_file): return exe_file From 37c070597cb83ad7f0fd378b5b786d856d707a43 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 31 Mar 2016 09:57:57 -0700 Subject: [PATCH 111/317] Fix tests on py26 the print() function does not appear to be mockable there --- letsencrypt/tests/hook_test.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py index 45025b054..6506eea2c 100644 --- a/letsencrypt/tests/hook_test.py +++ b/letsencrypt/tests/hook_test.py @@ -3,6 +3,7 @@ import os import unittest +import sys import mock @@ -76,10 +77,14 @@ class HookTest(unittest.TestCase): self.assertEqual(os.environ["RENEWED_DOMAINS"], "a b") self.assertEqual(os.environ["RENEWED_LINEAGE"], "thing") - with mock.patch("letsencrypt.hooks.print") as mock_print: - config = mock.MagicMock(renew_hook="true", dry_run=True) + config = mock.MagicMock(renew_hook="true", dry_run=True) + if sys.version_info < (2, 7): + # the print() function is not mockable in py26 self._test_a_hook(config, rhook, 0) - self.assertEqual(mock_print.call_count, 2) + else: + with mock.patch("letsencrypt.hooks.print") as mock_print: + self._test_a_hook(config, rhook, 0) + self.assertEqual(mock_print.call_count, 2) @mock.patch('letsencrypt.hooks.Popen') def test_run_hook(self, mock_popen): From 18e05cc28424d63312e62988bc922bac099458f0 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 31 Mar 2016 12:40:11 -0700 Subject: [PATCH 112/317] Document which hooks are run with --dry-run --- letsencrypt/cli.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 1091a804b..199b98311 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -558,7 +558,14 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): None, "--dry-run", action="store_true", dest="dry_run", help="Perform a test run of the client, obtaining test (invalid) certs" " but not saving them to disk. This can currently only be used" - " with the 'certonly' subcommand.") + " with the 'certonly' and 'renew' subcommands. \nNote: Although --dry-run" + " tries to avoid making any persistent changes on a system, it " + " is not completely side-effect free: if used with webserver authenticator plugins" + " like apache and nginx, it makes and then reverts temporary config changes" + " in order to obtain test certs, and reloads webservers to deploy and then" + " roll back those changes. It also calls --pre-hook and --post-hook commands" + " if they are defined because they may be necessary to accurately simulate" + " renewal. --renew-hook commands are not called.") helpful.add( None, "--register-unsafely-without-email", action="store_true", help="Specifying this flag enables registering an account with no " From 1802cee2e6c480bbcb6cfed0e859058f39afc0e7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 13:20:00 -0700 Subject: [PATCH 113/317] Remove commented out print debugging statements --- letsencrypt/cli.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 256e0c801..b8a17d70e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -481,12 +481,10 @@ class HelpfulArgumentParser(object): """ if self.visible_topics[topic]: - #print("Adding visible group " + topic) group = self.parser.add_argument_group(topic, **kwargs) self.groups[topic] = group return group else: - #print("Invisible group " + topic) return self.silent_parser def add_plugin_args(self, plugins): @@ -498,7 +496,6 @@ class HelpfulArgumentParser(object): """ for name, plugin_ep in six.iteritems(plugins): parser_or_group = self.add_group(name, description=plugin_ep.description) - #print(parser_or_group) plugin_ep.plugin_cls.inject_parser_options(parser_or_group, name) def determine_help_topics(self, chosen_topic): From bb426c8ae8b6bb02021fc326059a27946472b269 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Thu, 31 Mar 2016 22:43:16 +0200 Subject: [PATCH 114/317] Add MariaDB debugging to Travis build --- .travis.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6b325e985..7987c3917 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,15 +28,31 @@ matrix: include: - python: "2.6" env: TOXENV=py26 BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.6" env: TOXENV=py26-oldest BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.7" env: TOXENV=apacheconftest sudo: required - python: "2.7" env: TOXENV=py27 BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.7" env: TOXENV=py27-oldest BOULDER_INTEGRATION=1 + sudo: true + after_failure: + - sudo cat /var/log/mysql/error.log + - ps aux | grep mysql - python: "2.7" env: TOXENV=lint - sudo: required From 2d9860e2cea3dd85730572a4e0361823cced1360 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 15:31:26 -0700 Subject: [PATCH 115/317] Add _Default class --- letsencrypt/cli.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b8a17d70e..d997cb3a5 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -99,6 +99,10 @@ def usage_strings(plugins): return USAGE % (apache_doc, nginx_doc), SHORT_USAGE +class _Default(object): + """Trivial class used to detect if an option was set by the user.""" + + def set_by_cli(var): """ Return True if a particular config variable has been set by the user From 12082a94ad987e01ae0a985d69b56090e675ce37 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 18:08:36 -0700 Subject: [PATCH 116/317] Simplify default detection --- letsencrypt/cli.py | 99 ++++++++++++++++------------------- letsencrypt/tests/cli_test.py | 19 +++++++ 2 files changed, 63 insertions(+), 55 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index d997cb3a5..1cb29f331 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -86,6 +86,17 @@ More detailed help: """ +# These sets are used when to help detect options set by the user. +ARGPARSE_PARAMS_TO_REMOVE = set(("const", "nargs", "type",)) + + +EXIT_ACTIONS = set(("help", "version",)) + + +ZERO_ARG_ACTIONS = set(("store_const", "store_true", + "store_false", "append_const", "count",)) + + def usage_strings(plugins): """Make usage strings late so that plugins can be initialised late""" if "nginx" in plugins: @@ -100,7 +111,19 @@ def usage_strings(plugins): class _Default(object): - """Trivial class used to detect if an option was set by the user.""" + """A class to use as a default to detect if a value is set by a user""" + + def __bool__(self): + return False + + def __eq__(self, other): + return isinstance(other, _Default) + + def __hash__(self): + return id(_Default) + + def __nonzero__(self): + return self.__bool__() def set_by_cli(var): @@ -124,25 +147,8 @@ def set_by_cli(var): detector.installer = inst if inst else "" logger.debug("Default Detector is %r", detector) - try: - # Is detector.var something that isn't false? - change_detected = getattr(detector, var) - except AttributeError: - logger.warning("Missing default analysis for %r", var) - return False - - if change_detected: - return True - # Special case: we actually want account to be set to "" if the server - # the account was on has changed - elif var == "account" and (detector.server or detector.dry_run or detector.staging): - return True - # Special case: vars like --no-redirect that get set True -> False - # default to None; False means they were set - elif var in detector.store_false_vars and change_detected is not None: - return True - else: - return False + value = getattr(detector, var) + return not isinstance(value, _Default) # static housekeeping var set_by_cli.detector = None @@ -240,13 +246,7 @@ class HelpfulArgumentParser(object): self.parser._add_config_file_help = False # pylint: disable=protected-access self.silent_parser = SilentParser(self.parser) - # This setting attempts to force all default values to things that are - # pythonically false; it is used to detect when values have been - # explicitly set by the user, including when they are set to their - # normal default value self.detect_defaults = detect_defaults - if detect_defaults: - self.store_false_vars = {} # vars that use "store_false" self.args = args self.determine_verb() @@ -272,6 +272,9 @@ class HelpfulArgumentParser(object): parsed_args.func = self.VERBS[self.verb] parsed_args.verb = self.verb + if self.detect_defaults: + return parsed_args + # Do any post-parsing homework here # we get domains from -d, but also from the webroot map... @@ -307,9 +310,6 @@ class HelpfulArgumentParser(object): "cannot be used with --csr") self.handle_csr(parsed_args) - if self.detect_defaults: # plumbing - parsed_args.store_false_vars = self.store_false_vars - return parsed_args def handle_csr(self, parsed_args): @@ -416,7 +416,7 @@ class HelpfulArgumentParser(object): """ if self.detect_defaults: - kwargs = self.modify_arg_for_default_detection(self, *args, **kwargs) + kwargs = self.modify_kwargs_for_default_detection(**kwargs) if self.visible_topics[topic]: if topic in self.groups: @@ -428,39 +428,28 @@ class HelpfulArgumentParser(object): kwargs["help"] = argparse.SUPPRESS self.parser.add_argument(*args, **kwargs) + def modify_kwargs_for_default_detection(self, **kwargs): + """Modify an arg so we can check if it was set by the user. - def modify_arg_for_default_detection(self, *args, **kwargs): - """ - Adding an arg, but ensure that it has a default that evaluates to false, - so that set_by_cli can tell if it was set. Only called if detect_defaults==True. + Changes the parameters given to argparse when adding an argument + so we can properly detect if the value was set by the user. - :param list *args: the names of this argument flag - :param dict **kwargs: various argparse settings for this argument + :param dict kwargs: various argparse settings for this argument :returns: a modified versions of kwargs + :rtype: dict + """ - # argument either doesn't have a default, or the default doesn't - # isn't Pythonically false - if kwargs.get("default", True): - arg_type = kwargs.get("type", None) - if arg_type == int or kwargs.get("action", "") == "count": - kwargs["default"] = 0 - elif arg_type == read_file or "-c" in args: - kwargs["default"] = "" - kwargs["type"] = str - else: - kwargs["default"] = "" - # This doesn't matter at present (none of the store_false args - # are renewal-relevant), but implement it for future sanity: - # detect the setting of args whose presence causes True -> False - if kwargs.get("action", "") == "store_false": - kwargs["default"] = None - for var in args: - self.store_false_vars[var] = True + action = kwargs.get("action", None) + if action not in EXIT_ACTIONS: + kwargs["action"] = ("store_true" if action in ZERO_ARG_ACTIONS else + "store") + kwargs["default"] = _Default() + for param in ARGPARSE_PARAMS_TO_REMOVE: + kwargs.pop(param, None) return kwargs - def add_deprecated_argument(self, argument_name, num_args): """Adds a deprecated argument with the name argument_name. diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 04b5a2f3c..b0f3d236c 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -1023,5 +1023,24 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): self.assertEqual(result, (None, None)) +class DefaultTest(unittest.TestCase): + """Tests for letsencrypt.cli._Default.""" + + def setUp(self): + # pylint: disable=protected-access + self.default1 = cli._Default() + self.default2 = cli._Default() + + def test_boolean(self): + self.assertFalse(self.default1) + self.assertFalse(self.default2) + + def test_equality(self): + self.assertEqual(self.default1, self.default2) + + def test_hash(self): + self.assertEqual(hash(self.default1), hash(self.default2)) + + if __name__ == '__main__': unittest.main() # pragma: no cover From 40fd25c9e9ccb9ddc4e8682525530f27a240fbf3 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 18:12:11 -0700 Subject: [PATCH 117/317] Add HelpfulArgumentGroup --- letsencrypt/cli.py | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 1cb29f331..5b5ec3a92 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -197,21 +197,23 @@ def config_help(name, hidden=False): return interfaces.IConfig[name].__doc__ -class SilentParser(object): # pylint: disable=too-few-public-methods - """Silent wrapper around argparse. +class HelpfulArgumentGroup(object): + """Emulates an argparse group for use with HelpfulArgumentParser. - A mini parser wrapper that doesn't print help for its - arguments. This is needed for the use of callbacks to define - arguments within plugins. + This class is used in the add_group method of HelpfulArgumentParser. + Command line arguments can be added to the group, but help + suppression and default detection is applied by + HelpfulArgumentParser when necessary. """ - def __init__(self, parser): - self.parser = parser + def __init__(self, helpful_arg_parser, topic): + self._parser = helpful_arg_parser + self._topic = topic def add_argument(self, *args, **kwargs): - """Wrap, but silence help""" - kwargs["help"] = argparse.SUPPRESS - self.parser.add_argument(*args, **kwargs) + """Add a new command line argument to the argument group.""" + self._parser.add(self._topic, *args, **kwargs) + class HelpfulArgumentParser(object): """Argparse Wrapper. @@ -244,7 +246,6 @@ class HelpfulArgumentParser(object): # This is the only way to turn off overly verbose config flag documentation self.parser._add_config_file_help = False # pylint: disable=protected-access - self.silent_parser = SilentParser(self.parser) self.detect_defaults = detect_defaults @@ -465,20 +466,22 @@ class HelpfulArgumentParser(object): self.parser.add_argument, argument_name, num_args) def add_group(self, topic, **kwargs): - """ + """Create a new argument group. - This has to be called once for every topic; but we leave those calls - next to the argument definitions for clarity. Return something - arguments can be added to if necessary, either the parser or an argument - group. + This method must be called once for every topic, however, calls + to this function are left next to the argument definitions for + clarity. + + :param str topic: Name of the new argument group. + + :returns: The new argument group. + :rtype: `HelpfulArgumentGroup` """ if self.visible_topics[topic]: - group = self.parser.add_argument_group(topic, **kwargs) - self.groups[topic] = group - return group - else: - return self.silent_parser + self.groups[topic] = self.parser.add_argument_group(topic, **kwargs) + + return HelpfulArgumentGroup(self, topic) def add_plugin_args(self, plugins): """ From b748d3979579e58ff22a9af1fcbd2f4ab59cd661 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 18:49:23 -0700 Subject: [PATCH 118/317] Create dict for to store flag interactions --- letsencrypt/cli.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 5b5ec3a92..646d14ca0 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -97,6 +97,12 @@ ZERO_ARG_ACTIONS = set(("store_const", "store_true", "store_false", "append_const", "count",)) +# Maps a config option to a list of config options that may have modified it. +# This dictionary is used recursively, so if A modifies B and B modifies C, +# it is determined that C was modified by the user if A was modified. +VAR_MODIFIERS = {"account": ["server"], "server": ["dry_run", "staging"]} + + def usage_strings(plugins): """Make usage strings late so that plugins can be initialised late""" if "nginx" in plugins: @@ -147,8 +153,14 @@ def set_by_cli(var): detector.installer = inst if inst else "" logger.debug("Default Detector is %r", detector) - value = getattr(detector, var) - return not isinstance(value, _Default) + if not isinstance(getattr(detector, var), _Default): + return True + + for modifier in VAR_MODIFIERS.get(var, []): + if set_by_cli(modifier): + return True + + return False # static housekeeping var set_by_cli.detector = None From 98493f72b621ba783ecd309c39d19f73d839b03a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 18:53:31 -0700 Subject: [PATCH 119/317] Simplify plugin default detection --- letsencrypt/cli.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 646d14ca0..a302576f7 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -148,9 +148,8 @@ def set_by_cli(var): detector = set_by_cli.detector = prepare_and_parse_args( plugins, reconstructed_args, detect_defaults=True) # propagate plugin requests: eg --standalone modifies config.authenticator - auth, inst = plugin_selection.cli_plugin_requests(detector) - detector.authenticator = auth if auth else "" - detector.installer = inst if inst else "" + detector.authenticator, detector.installer = ( + plugin_selection.cli_plugin_requests(detector)) logger.debug("Default Detector is %r", detector) if not isinstance(getattr(detector, var), _Default): From 42c16387721f322b98ae003d42d7cbdd839f9a0e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 19:13:35 -0700 Subject: [PATCH 120/317] Add webroot_map to default detection --- letsencrypt/cli.py | 3 ++- letsencrypt/renewal.py | 8 +++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index a302576f7..7408f84d2 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -100,7 +100,8 @@ ZERO_ARG_ACTIONS = set(("store_const", "store_true", # Maps a config option to a list of config options that may have modified it. # This dictionary is used recursively, so if A modifies B and B modifies C, # it is determined that C was modified by the user if A was modified. -VAR_MODIFIERS = {"account": ["server"], "server": ["dry_run", "staging"]} +VAR_MODIFIERS = {"account": ["server"], "server": ["dry_run", "staging"], + "webroot_map": ["webroot_path"]} def usage_strings(plugins): diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 27546bec9..b6bec80c2 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -96,16 +96,14 @@ def _restore_webroot_config(config, renewalparams): form. """ if "webroot_map" in renewalparams: - # if the user does anything that would create a new webroot map on the - # CLI, don't use the old one - if not (cli.set_by_cli("webroot_map") or cli.set_by_cli("webroot_path")): - setattr(config.namespace, "webroot_map", renewalparams["webroot_map"]) + if not cli.set_by_cli("webroot_map"): + config.namespace.webroot_map = renewalparams["webroot_map"] elif "webroot_path" in renewalparams: logger.info("Ancient renewal conf file without webroot-map, restoring webroot-path") wp = renewalparams["webroot_path"] if isinstance(wp, str): # prior to 0.1.0, webroot_path was a string wp = [wp] - setattr(config.namespace, "webroot_path", wp) + config.namespace.webroot_path = wp def _restore_plugin_configs(config, renewalparams): From 8efc3ae7c83406b3ce4b695af052a12095607709 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 20:07:18 -0700 Subject: [PATCH 121/317] Add report_config_interaction --- letsencrypt/cli.py | 21 +++++++++++++ letsencrypt/tests/cli_test.py | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 7408f84d2..1a28848ac 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -104,6 +104,27 @@ VAR_MODIFIERS = {"account": ["server"], "server": ["dry_run", "staging"], "webroot_map": ["webroot_path"]} +def report_config_interaction(modified, modifiers): + """Registers config option interaction to be checked by set_by_cli. + + This function can be called by during the __init__ method of plugins + to register interactions between config options. + + :param modified: config options that can be modified by modifiers + :type modified: iterable or str + :param modifiers: config options that modify modified + :type modifiers: iterable or str + + """ + if isinstance(modified, str): + modified = [modified] + if isinstance(modifiers, str): + modifiers = [modifiers] + + for var in modified: + VAR_MODIFIERS.setdefault(var, []).extend(modifiers) + + def usage_strings(plugins): """Make usage strings late so that plugins can be initialised late""" if "nginx" in plugins: diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index b0f3d236c..f1ce9233d 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -12,6 +12,7 @@ import unittest import mock import six +from six.moves import reload_module # pylint: disable=import-error from acme import jose @@ -1042,5 +1043,60 @@ class DefaultTest(unittest.TestCase): self.assertEqual(hash(self.default1), hash(self.default2)) +class SetByCliTest(unittest.TestCase): + """Tests for letsencrypt.set_by_cli and related functions.""" + + def setUp(self): + reload_module(cli) + + def test_webroot_map(self): + args = '-w /var/www/html -d example.com'.split() + verb = 'renew' + self.assertTrue(_call_set_by_cli('webroot_map', args, verb)) + + def test_report_config_interaction_str(self): + cli.report_config_interaction('manual_public_ip_logging_ok', + 'manual_test_mode') + cli.report_config_interaction('manual_test_mode', 'manual') + + self._test_report_config_interaction_common() + + def test_report_config_interaction_iterable(self): + cli.report_config_interaction(('manual_public_ip_logging_ok',), + ('manual_test_mode',)) + cli.report_config_interaction(('manual_test_mode',), ('manual',)) + + self._test_report_config_interaction_common() + + def _test_report_config_interaction_common(self): + """Tests implied interaction between manual flags. + + --manual implies --manual-test-mode which implies + --manual-public-ip-logging-ok. These interactions don't actually + exist in the client, but are used here for testing purposes. + + """ + + args = ['--manual'] + verb = 'renew' + for v in ('manual', 'manual_test_mode', 'manual_public_ip_logging_ok'): + self.assertTrue(_call_set_by_cli(v, args, verb)) + + cli.set_by_cli.detector = None + + args = ['--manual-test-mode'] + for v in ('manual_test_mode', 'manual_public_ip_logging_ok'): + self.assertTrue(_call_set_by_cli(v, args, verb)) + + self.assertFalse(_call_set_by_cli('manual', args, verb)) + + +def _call_set_by_cli(var, args, verb): + with mock.patch('letsencrypt.cli.helpful_parser') as mock_parser: + mock_parser.args = args + mock_parser.verb = verb + return cli.set_by_cli(var) + + if __name__ == '__main__': unittest.main() # pragma: no cover From 7e2e4192b443766bc6e2f29bf400a1a9774f4bbe Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 20:11:53 -0700 Subject: [PATCH 122/317] Update add_parser_arguments comment --- letsencrypt/plugins/common.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/letsencrypt/plugins/common.py b/letsencrypt/plugins/common.py index a9410d514..c66857096 100644 --- a/letsencrypt/plugins/common.py +++ b/letsencrypt/plugins/common.py @@ -45,15 +45,14 @@ class Plugin(object): def add_parser_arguments(cls, add): """Add plugin arguments to the CLI argument parser. + NOTE: If some of your flags interact with others, you can + use cli.report_config_interaction to register this to ensure + values are correctly saved/overridable during renewal. + :param callable add: Function that proxies calls to `argparse.ArgumentParser.add_argument` prepending options with unique plugin name prefix. - NOTE: if you add argpase arguments such that users setting them can - create a config entry that python's bool() would consider false (ie, - the use might set the variable to "", [], 0, etc), please ensure that - cli.set_by_cli() works for your variable. - """ @classmethod From 90f6ed26889de12c7c96833d6a2a99c7ffc54fc6 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 20:19:14 -0700 Subject: [PATCH 123/317] Use sets to prevent duplicates --- letsencrypt/cli.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 1a28848ac..d2bcce00b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -97,11 +97,12 @@ ZERO_ARG_ACTIONS = set(("store_const", "store_true", "store_false", "append_const", "count",)) -# Maps a config option to a list of config options that may have modified it. +# Maps a config option to a set of config options that may have modified it. # This dictionary is used recursively, so if A modifies B and B modifies C, # it is determined that C was modified by the user if A was modified. -VAR_MODIFIERS = {"account": ["server"], "server": ["dry_run", "staging"], - "webroot_map": ["webroot_path"]} +VAR_MODIFIERS = {"account": set(("server",)), + "server": set(("dry_run", "staging",)), + "webroot_map": set(("webroot_path",))} def report_config_interaction(modified, modifiers): @@ -117,12 +118,12 @@ def report_config_interaction(modified, modifiers): """ if isinstance(modified, str): - modified = [modified] + modified = (modified,) if isinstance(modifiers, str): - modifiers = [modifiers] + modifiers = (modifiers,) for var in modified: - VAR_MODIFIERS.setdefault(var, []).extend(modifiers) + VAR_MODIFIERS.setdefault(var, set()).update(modifiers) def usage_strings(plugins): From 5a097f2fabded7f22a957f29b6789e27033cfa8a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 20:25:25 -0700 Subject: [PATCH 124/317] set set to tuple3 --- letsencrypt/cli.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index d2bcce00b..ac5e21d7a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -86,10 +86,11 @@ More detailed help: """ +# These argparse parameters should be removed when detecting defaults. +ARGPARSE_PARAMS_TO_REMOVE = ("const", "nargs", "type",) + + # These sets are used when to help detect options set by the user. -ARGPARSE_PARAMS_TO_REMOVE = set(("const", "nargs", "type",)) - - EXIT_ACTIONS = set(("help", "version",)) From 42f990d31f06499becf3f630332bdb10ec2c3448 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 31 Mar 2016 21:51:15 -0700 Subject: [PATCH 125/317] comment++ --- letsencrypt/cli.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index ac5e21d7a..7e572cc1d 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -109,8 +109,9 @@ VAR_MODIFIERS = {"account": set(("server",)), def report_config_interaction(modified, modifiers): """Registers config option interaction to be checked by set_by_cli. - This function can be called by during the __init__ method of plugins - to register interactions between config options. + This function can be called by during the __init__ or + add_parser_arguments methods of plugins to register interactions + between config options. :param modified: config options that can be modified by modifiers :type modified: iterable or str From b5acb8b71e3daf284451a705386aee4964651802 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 10:53:14 -0700 Subject: [PATCH 126/317] add add domain --- letsencrypt/cli.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 256e0c801..587a66faf 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -876,6 +876,22 @@ class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstrin args.webroot_path.append(webroot) +def add_domain(args_or_config, domain): + """Registers a new domain to be used during the current client run. + + If all domains in domain have been registered, this function has no + effect. + + :param args_or_config: parsed command line arguments + :type args_or_config: argparse.Namespace or + configuration.NamespaceConfig + :param str domain: one or more comma separated domains + + """ + args_or_config.domains.extend(le_util.enforce_domain_sanity(d.strip()) + for d in domain.split(",") if d not in args_or_config.domains) + + def process_domain(args_or_config, domain_arg, webroot_path=None): """ Process a new -d flag, helping the webroot plugin construct a map of From ca7049dabcdabb8182ebb6853a720ad62cafc764 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 13:21:57 -0700 Subject: [PATCH 127/317] add webroot_path parsing functions to webroot.py --- letsencrypt/plugins/webroot.py | 30 +++++++++++++++++++++++++++-- letsencrypt/plugins/webroot_test.py | 5 ----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 6d2899511..2d87d3475 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -57,8 +57,6 @@ to serve all files under specified web root ({0}).""" "--webroot-path and --domains, or --webroot-map. Run with " " --help webroot for examples.") for name, path in path_map.items(): - if not os.path.isdir(path): - raise errors.PluginError(path + " does not exist or is not a directory") self.full_roots[name] = os.path.join(path, challenges.HTTP01.URI_ROOT_PATH) logger.debug("Creating root challenges validation dir at %s", @@ -157,3 +155,31 @@ to serve all files under specified web root ({0}).""" root_path) else: raise + + +def _match_webroot_with_domains(args_or_config): + """Applies the most recent webroot path to all unmatched domains. + + :param args_or_config: parsed command line arguments + :type args_or_config: argparse.Namespace or + configuration.NamespaceConfig + + """ + webroot_path = args_or_config.webroot_path[-1] + for domain in args_or_config.domains: + args_or_config.webroot_map.set_default(domain, webroot_path) + + +def _validate_webroot(webroot_path): + """Validates and returns the absolute path of webroot_path. + + :param str webroot_path: path to the webroot directory + + :returns: absolute path of webroot_path + :rtype: str + + """ + if not os.path.isdir(webroot_path): + raise errors.PluginError(webroot_path + " does not exist or is not a directory") + + return os.path.abspath(webroot_path) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index ed0326555..e80537260 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -55,11 +55,6 @@ class AuthenticatorTest(unittest.TestCase): self.auth.add_parser_arguments(add) self.assertEqual(0, add.call_count) # args moved to cli.py! - def test_prepare_bad_root(self): - self.config.webroot_path = os.path.join(self.path, "null") - self.config.webroot_map["thing.com"] = self.config.webroot_path - self.assertRaises(errors.PluginError, self.auth.prepare) - def test_prepare_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} From d3fa0dd222fff0cde22b247afcf4bfa5b1dd3773 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 13:51:26 -0700 Subject: [PATCH 128/317] make add_domain more useful --- letsencrypt/cli.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 587a66faf..b9416f13f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -876,20 +876,29 @@ class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstrin args.webroot_path.append(webroot) -def add_domain(args_or_config, domain): - """Registers a new domain to be used during the current client run. +def add_domains(args_or_config, domains): + """Registers new domains to be used during the current client run. - If all domains in domain have been registered, this function has no - effect. + Domains are not added to the list of requested domains if they have + already been registered. :param args_or_config: parsed command line arguments :type args_or_config: argparse.Namespace or configuration.NamespaceConfig :param str domain: one or more comma separated domains + :returns: domains after they have been normalized and validated + :rtype: `list` of `str` + """ - args_or_config.domains.extend(le_util.enforce_domain_sanity(d.strip()) - for d in domain.split(",") if d not in args_or_config.domains) + validated_domains = [] + for domain in domains.split(","): + domain = le_util.enforce_domain_sanity(domain.strip()) + validated_domains.append(domain) + if domain not in args_or_config.domains: + args_or_config.domains.append(domain) + + return validated_domains def process_domain(args_or_config, domain_arg, webroot_path=None): From c83c09e12bd0f73562e6709da2b21f26e88fbb7a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 14:07:42 -0700 Subject: [PATCH 129/317] Add _WebrootMapAction to webroot.py --- letsencrypt/plugins/webroot.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 2d87d3475..336a58f19 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -1,5 +1,7 @@ """Webroot plugin.""" +import argparse import errno +import json import logging import os from collections import defaultdict @@ -9,6 +11,7 @@ import six from acme import challenges +from letsencrypt import cli from letsencrypt import errors from letsencrypt import interfaces from letsencrypt.plugins import common @@ -157,6 +160,17 @@ to serve all files under specified web root ({0}).""" raise +class _WebrootMapAction(argparse.Action): + """Action class for parsing webroot_map.""" + + def __call__(self, parser, namespace, webroot_map, option_string=None): + for domains, webroot_path in six.iteritems(json.loads(webroot_map)): + validated_webroot_path = _validate_webroot(webroot_path) + namespace.webroot_map.update( + (d, validated_webroot_path,) + for d in cli.add_domains(namespace, domains)) + + def _match_webroot_with_domains(args_or_config): """Applies the most recent webroot path to all unmatched domains. From f663a6f9611d13d28028b8b6ae921e55dd979d35 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 14:24:52 -0700 Subject: [PATCH 130/317] add _WebrootPathAction to webroot.py --- letsencrypt/plugins/webroot.py | 40 ++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 336a58f19..9a16a4ba5 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -171,17 +171,26 @@ class _WebrootMapAction(argparse.Action): for d in cli.add_domains(namespace, domains)) -def _match_webroot_with_domains(args_or_config): - """Applies the most recent webroot path to all unmatched domains. +class _WebrootPathAction(argparse.Action): + """Action class for parsing webroot_path.""" + def __init__(self, *args, **kwargs): + super(_WebrootPathAction, self).__init__(*args, **kwargs) + self._domain_before_webroot = False - :param args_or_config: parsed command line arguments - :type args_or_config: argparse.Namespace or - configuration.NamespaceConfig + def __call__(self, parser, namespace, webroot_path, option_string=None): + if self._domain_before_webroot: + raise errors.PluginError( + "If you specify multiple webroot paths, " + "one of them must precede all domain flags") - """ - webroot_path = args_or_config.webroot_path[-1] - for domain in args_or_config.domains: - args_or_config.webroot_map.set_default(domain, webroot_path) + if namespace.webroot_path: + # Apply previous webroot to all matched + # domains before setting the new webroot path + _match_webroot_with_domains(namespace) + elif namespace.domains: + self._domain_before_webroot = True + + namespace.webroot_path.append(_validate_webroot(webroot_path)) def _validate_webroot(webroot_path): @@ -197,3 +206,16 @@ def _validate_webroot(webroot_path): raise errors.PluginError(webroot_path + " does not exist or is not a directory") return os.path.abspath(webroot_path) + + +def _match_webroot_with_domains(args_or_config): + """Applies the most recent webroot path to all unmatched domains. + + :param args_or_config: parsed command line arguments + :type args_or_config: argparse.Namespace or + configuration.NamespaceConfig + + """ + webroot_path = args_or_config.webroot_path[-1] + for domain in args_or_config.domains: + args_or_config.webroot_map.set_default(domain, webroot_path) From b1bdc4590de09db15473501a4b6e8f6ffae18f90 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 16:16:17 -0700 Subject: [PATCH 131/317] Move webroot processing to webroot.py --- letsencrypt/cli.py | 101 +++------------------------- letsencrypt/main.py | 10 +-- letsencrypt/plugins/webroot.py | 25 +++++-- letsencrypt/plugins/webroot_test.py | 2 +- letsencrypt/renewal.py | 5 +- letsencrypt/tests/cli_test.py | 54 --------------- letsencrypt/tests/client_test.py | 6 +- 7 files changed, 38 insertions(+), 165 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index b9416f13f..5e192424a 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -2,7 +2,6 @@ from __future__ import print_function import argparse import glob -import json import logging import logging.handlers import os @@ -270,12 +269,6 @@ class HelpfulArgumentParser(object): # Do any post-parsing homework here - # we get domains from -d, but also from the webroot map... - if parsed_args.webroot_map: - for domain in parsed_args.webroot_map.keys(): - if domain not in parsed_args.domains: - parsed_args.domains.append(domain) - if parsed_args.staging or parsed_args.dry_run: if parsed_args.server not in (flag_default("server"), constants.STAGING_URI): conflicts = ["--staging"] if parsed_args.staging else [] @@ -311,7 +304,7 @@ class HelpfulArgumentParser(object): def handle_csr(self, parsed_args): """ Process a --csr flag. This needs to happen early enough that the - webroot plugin can know about the calls to process_domain + webroot plugin can know about the calls to add_domains. """ if parsed_args.verb != "certonly": raise errors.Error("Currently, a CSR file may only be specified " @@ -333,14 +326,9 @@ class HelpfulArgumentParser(object): logger.debug("DER CSR parse error %s", e1) logger.debug("PEM CSR parse error %s", traceback.format_exc()) raise errors.Error("Failed to parse CSR file: {0}".format(parsed_args.csr[0])) - for d in domains: - process_domain(parsed_args, d) for d in domains: - sanitised = le_util.enforce_domain_sanity(d) - if d.lower() != sanitised: - raise errors.ConfigurationError( - "CSR domain {0} needs to be sanitised to {1}.".format(d, sanitised)) + add_domains(parsed_args, d) if not domains: # TODO: add CN to domains instead: @@ -572,7 +560,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): #for subparser in parser_run, parser_auth, parser_install: # subparser.add_argument("domains", nargs="*", metavar="domain") helpful.add(None, "-d", "--domains", "--domain", dest="domains", - metavar="DOMAIN", action=DomainFlagProcessor, default=[], + metavar="DOMAIN", action=_DomainsAction, default=[], help="Domain names to apply. For multiple domains you can use " "multiple -d flags or enter a comma separated list of domains " "as a parameter.") @@ -829,51 +817,13 @@ def _plugins_parsing(helpful, plugins): helpful.add_plugin_args(plugins) - # These would normally be a flag within the webroot plugin, but because - # they are parsed in conjunction with --domains, they live here for - # legibility. helpful.add_plugin_ags must be called first to add the - # "webroot" topic - helpful.add("webroot", "-w", "--webroot-path", default=[], action=WebrootPathProcessor, - help="public_html / webroot path. This can be specified multiple times to " - "handle different domains; each domain will have the webroot path that" - " preceded it. For instance: `-w /var/www/example -d example.com -d " - "www.example.com -w /var/www/thing -d thing.net -d m.thing.net`") - # --webroot-map still has some awkward properties, so it is undocumented - helpful.add("webroot", "--webroot-map", default={}, action=WebrootMapProcessor, - help="JSON dictionary mapping domains to webroot paths; this " - "implies -d for each entry. You may need to escape this " - "from your shell. E.g.: --webroot-map " - """'{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}' """ - "This option is merged with, but takes precedence over, " - "-w / -d entries. At present, if you put webroot-map in " - "a config file, it needs to be on a single line, like: " - 'webroot-map = {"example.com":"/var/www"}.') +class _DomainsAction(argparse.Action): + """Action class for parsing domains.""" -class WebrootPathProcessor(argparse.Action): # pylint: disable=missing-docstring - def __init__(self, *args, **kwargs): - self.domain_before_webroot = False - argparse.Action.__init__(self, *args, **kwargs) - - def __call__(self, parser, args, webroot, option_string=None): - """ - Keep a record of --webroot-path / -w flags during processing, so that - we know which apply to which -d flags - """ - if not args.webroot_path: # first -w flag encountered - # if any --domain flags preceded the first --webroot-path flag, - # apply that webroot path to those; subsequent entries in - # args.webroot_map are filled in by cli.DomainFlagProcessor - if args.domains: - self.domain_before_webroot = True - for d in args.domains: - args.webroot_map.setdefault(d, webroot) - elif self.domain_before_webroot: - # FIXME if you set domains in a args file, you should get a different error - # here, pointing you to --webroot-map - raise errors.Error("If you specify multiple webroot paths, one of " - "them must precede all domain flags") - args.webroot_path.append(webroot) + def __call__(self, parser, namespace, domain, option_string=None): + """Just wrap add_domains in argparseese.""" + add_domains(namespace, domain) def add_domains(args_or_config, domains): @@ -899,38 +849,3 @@ def add_domains(args_or_config, domains): args_or_config.domains.append(domain) return validated_domains - - -def process_domain(args_or_config, domain_arg, webroot_path=None): - """ - Process a new -d flag, helping the webroot plugin construct a map of - {domain : webrootpath} if -w / --webroot-path is in use - - :param args_or_config: may be an argparse args object, or a NamespaceConfig object - :param str domain_arg: a string representing 1+ domains, eg: "eg.is, example.com" - :param str webroot_path: (optional) the webroot_path for these domains - - """ - webroot_path = webroot_path if webroot_path else args_or_config.webroot_path - - for domain in (d.strip() for d in domain_arg.split(",")): - domain = le_util.enforce_domain_sanity(domain) - if domain not in args_or_config.domains: - args_or_config.domains.append(domain) - # Each domain has a webroot_path of the most recent -w flag - # unless it was explicitly included in webroot_map - if webroot_path: - args_or_config.webroot_map.setdefault(domain, webroot_path[-1]) - - -class WebrootMapProcessor(argparse.Action): # pylint: disable=missing-docstring - def __call__(self, parser, args, webroot_map_arg, option_string=None): - webroot_map = json.loads(webroot_map_arg) - for domains, webroot_path in six.iteritems(webroot_map): - process_domain(args, domains, [webroot_path]) - - -class DomainFlagProcessor(argparse.Action): # pylint: disable=missing-docstring - def __call__(self, parser, args, domain_arg, option_string=None): - """Just wrap process_domain in argparseese.""" - process_domain(args, domain_arg) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 0afccc85e..ad4e5d2cf 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -288,14 +288,10 @@ def _find_duplicative_certs(config, domains): def _find_domains(config, installer): - if not config.domains: - domains = display_ops.choose_names(installer) - # record in config.domains (so that it can be serialised in renewal config files), - # and set webroot_map entries if applicable - for d in domains: - cli.process_domain(config, d) - else: + if config.domains: domains = config.domains + else: + domains = display_ops.choose_names(installer) if not domains: raise errors.Error("Please specify --domains, or --installer that " diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 9a16a4ba5..7c9aef509 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -38,9 +38,21 @@ to serve all files under specified web root ({0}).""" @classmethod def add_parser_arguments(cls, add): - # --webroot-path and --webroot-map are added in cli.py because they - # are parsed in conjunction with --domains - pass + add("path", "-w", default=[], action=_WebrootPathAction, + help="public_html / webroot path. This can be specified multiple " + "times to handle different domains; each domain will have " + "the webroot path that preceded it. For instance: `-w " + "/var/www/example -d example.com -d www.example.com -w " + "/var/www/thing -d thing.net -d m.thing.net`") + add("map", default={}, action=_WebrootMapAction, + help="JSON dictionary mapping domains to webroot paths; this " + "implies -d for each entry. You may need to escape this from " + "your shell. E.g.: --webroot-map " + '\'{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}\' ' + "This option is merged with, but takes precedence over, -w / " + "-d entries. At present, if you put webroot-map in a config " + "file, it needs to be on a single line, like: webroot-map = " + '{"example.com":"/var/www"}.') def get_chall_pref(self, domain): # pragma: no cover # pylint: disable=missing-docstring,no-self-use,unused-argument @@ -52,8 +64,10 @@ to serve all files under specified web root ({0}).""" self.performed = defaultdict(set) def prepare(self): # pylint: disable=missing-docstring - path_map = self.conf("map") + if self.conf("path"): + _match_webroot_with_domains(self.config) + path_map = self.conf("map") if not path_map: raise errors.PluginError( "Missing parts of webroot configuration; please set either " @@ -173,6 +187,7 @@ class _WebrootMapAction(argparse.Action): class _WebrootPathAction(argparse.Action): """Action class for parsing webroot_path.""" + def __init__(self, *args, **kwargs): super(_WebrootPathAction, self).__init__(*args, **kwargs) self._domain_before_webroot = False @@ -218,4 +233,4 @@ def _match_webroot_with_domains(args_or_config): """ webroot_path = args_or_config.webroot_path[-1] for domain in args_or_config.domains: - args_or_config.webroot_map.set_default(domain, webroot_path) + args_or_config.webroot_map.setdefault(domain, webroot_path) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index e80537260..23aabd82d 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -53,7 +53,7 @@ class AuthenticatorTest(unittest.TestCase): def test_add_parser_arguments(self): add = mock.MagicMock() self.auth.add_parser_arguments(add) - self.assertEqual(0, add.call_count) # args moved to cli.py! + self.assertEqual(2, add.call_count) def test_prepare_missing_root(self): self.config.webroot_path = None diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 27546bec9..2fa921906 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -12,6 +12,7 @@ import zope.component from letsencrypt import configuration from letsencrypt import cli from letsencrypt import errors +from letsencrypt import le_util from letsencrypt import storage from letsencrypt.plugins import disco as plugins_disco @@ -78,8 +79,8 @@ def _reconstitute(config, full_path): return None try: - for d in renewal_candidate.names(): - cli.process_domain(config, d) + config.domains = [le_util.enforce_domain_sanity(d) + for d in renewal_candidate.names()] except errors.ConfigurationError as error: logger.warning("Renewal configuration file %s references a cert " "that contains an invalid domain name. The problem " diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 04b5a2f3c..71c6356bb 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -459,60 +459,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods conflicts += ['--staging'] self._check_server_conflict_message(short_args, conflicts) - def _webroot_map_test(self, map_arg, path_arg, domains_arg, # pylint: disable=too-many-arguments - expected_map, expectect_domains, extra_args=None): - parse = self._get_argument_parser() - webroot_map_args = extra_args if extra_args else [] - if map_arg: - webroot_map_args.extend(["--webroot-map", map_arg]) - if path_arg: - webroot_map_args.extend(["-w", path_arg]) - if domains_arg: - webroot_map_args.extend(["-d", domains_arg]) - namespace = parse(webroot_map_args) - domains = main._find_domains(namespace, mock.MagicMock()) # pylint: disable=protected-access - self.assertEqual(namespace.webroot_map, expected_map) - self.assertEqual(set(domains), set(expectect_domains)) - - def test_parse_webroot(self): - parse = self._get_argument_parser() - webroot_args = ['--webroot', '-w', '/var/www/example', - '-d', 'example.com,www.example.com', '-w', '/var/www/superfluous', - '-d', 'superfluo.us', '-d', 'www.superfluo.us'] - namespace = parse(webroot_args) - self.assertEqual(namespace.webroot_map, { - 'example.com': '/var/www/example', - 'www.example.com': '/var/www/example', - 'www.superfluo.us': '/var/www/superfluous', - 'superfluo.us': '/var/www/superfluous'}) - - webroot_args = ['-d', 'stray.example.com'] + webroot_args - self.assertRaises(errors.Error, parse, webroot_args) - - simple_map = '{"eg.com" : "/tmp"}' - expected_map = {"eg.com": "/tmp"} - self._webroot_map_test(simple_map, None, None, expected_map, ["eg.com"]) - - # test merging webroot maps from the cli and a webroot map - expected_map["eg2.com"] = "/tmp2" - domains = ["eg.com", "eg2.com"] - self._webroot_map_test(simple_map, "/tmp2", "eg2.com,eg.com", expected_map, domains) - - # test inclusion of interactively specified domains in the webroot map - with mock.patch('letsencrypt.display.ops.choose_names') as mock_choose: - mock_choose.return_value = domains - expected_map["eg2.com"] = "/tmp" - self._webroot_map_test(None, "/tmp", None, expected_map, domains) - - extra_args = ['-c', test_util.vector_path('webrootconftest.ini')] - self._webroot_map_test(None, None, None, expected_map, domains, extra_args) - - webroot_map_args = ['--webroot-map', - '{"eg.com.,www.eg.com": "/tmp", "eg.is.": "/tmp2"}'] - namespace = parse(webroot_map_args) - self.assertEqual(namespace.webroot_map, - {"eg.com": "/tmp", "www.eg.com": "/tmp", "eg.is": "/tmp2"}) - def _certonly_new_request_common(self, mock_client, args=None): with mock.patch('letsencrypt.main._treat_as_renewal') as mock_renewal: mock_renewal.return_value = ("newcert", None) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index ed4e5def0..00e03e7e4 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -135,8 +135,8 @@ class ClientTest(unittest.TestCase): # FIXME move parts of this to test_cli.py... @mock.patch("letsencrypt.client.logger") - @mock.patch("letsencrypt.cli.process_domain") - def test_obtain_certificate_from_csr(self, mock_process_domain, mock_logger): + @mock.patch("letsencrypt.cli.add_domains") + def test_obtain_certificate_from_csr(self, mock_add_domains, mock_logger): self._mock_obtain_certificate() from letsencrypt import cli test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN) @@ -151,7 +151,7 @@ class ClientTest(unittest.TestCase): cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args) # make sure cli processing occurred - cli_processed = (call[0][1] for call in mock_process_domain.call_args_list) + cli_processed = (call[0][1] for call in mock_add_domains.call_args_list) self.assertEqual(set(cli_processed), set(("example.com", "www.example.com"))) # Now provoke an inconsistent domains error... mock_parsed_args.domains.append("hippopotamus.io") From e2af5ab9b495ca22009b5b251cd2d1df48635872 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Fri, 1 Apr 2016 16:42:44 -0700 Subject: [PATCH 132/317] updated docs with s/letsencrypt/certbot/g and more --- docs/api/account.rst | 4 +- docs/api/achallenges.rst | 4 +- docs/api/auth_handler.rst | 4 +- docs/api/cb_util.rst | 5 ++ docs/api/client.rst | 4 +- docs/api/configuration.rst | 4 +- docs/api/constants.rst | 4 +- docs/api/continuity_auth.rst | 4 +- docs/api/crypto_util.rst | 4 +- docs/api/display.rst | 16 ++-- docs/api/errors.rst | 4 +- docs/api/index.rst | 4 +- docs/api/interfaces.rst | 4 +- docs/api/le_util.rst | 5 -- docs/api/log.rst | 4 +- docs/api/plugins/common.rst | 4 +- docs/api/plugins/disco.rst | 4 +- docs/api/plugins/manual.rst | 4 +- docs/api/plugins/standalone.rst | 4 +- docs/api/plugins/util.rst | 4 +- docs/api/plugins/webroot.rst | 4 +- docs/api/proof_of_possession.rst | 4 +- docs/api/reporter.rst | 4 +- docs/api/reverter.rst | 4 +- docs/api/storage.rst | 4 +- docs/ciphers.rst | 80 ++++++++--------- docs/contributing.rst | 60 ++++++------- docs/index.rst | 2 +- docs/man/certbot.rst | 1 + docs/man/letsencrypt.rst | 1 - docs/packaging.rst | 2 +- docs/using.rst | 146 +++++++++++++++---------------- 32 files changed, 201 insertions(+), 205 deletions(-) create mode 100644 docs/api/cb_util.rst delete mode 100644 docs/api/le_util.rst create mode 100644 docs/man/certbot.rst delete mode 100644 docs/man/letsencrypt.rst diff --git a/docs/api/account.rst b/docs/api/account.rst index 16c2061a8..fd90230ea 100644 --- a/docs/api/account.rst +++ b/docs/api/account.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.account` +:mod:`certbot.account` -------------------------- -.. automodule:: letsencrypt.account +.. automodule:: certbot.account :members: diff --git a/docs/api/achallenges.rst b/docs/api/achallenges.rst index 09cec1702..90dda3f06 100644 --- a/docs/api/achallenges.rst +++ b/docs/api/achallenges.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.achallenges` +:mod:`certbot.achallenges` ------------------------------ -.. automodule:: letsencrypt.achallenges +.. automodule:: certbot.achallenges :members: diff --git a/docs/api/auth_handler.rst b/docs/api/auth_handler.rst index 3b168faf8..8819bb1bd 100644 --- a/docs/api/auth_handler.rst +++ b/docs/api/auth_handler.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.auth_handler` +:mod:`certbot.auth_handler` ------------------------------- -.. automodule:: letsencrypt.auth_handler +.. automodule:: certbot.auth_handler :members: diff --git a/docs/api/cb_util.rst b/docs/api/cb_util.rst new file mode 100644 index 000000000..066fa906c --- /dev/null +++ b/docs/api/cb_util.rst @@ -0,0 +1,5 @@ +:mod:`certbot.cb_util` +-------------------------- + +.. automodule:: certbot.cb_util + :members: diff --git a/docs/api/client.rst b/docs/api/client.rst index 7fe44df50..00a443cd9 100644 --- a/docs/api/client.rst +++ b/docs/api/client.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.client` +:mod:`certbot.client` ------------------------- -.. automodule:: letsencrypt.client +.. automodule:: certbot.client :members: diff --git a/docs/api/configuration.rst b/docs/api/configuration.rst index e92392b99..4e99c73d2 100644 --- a/docs/api/configuration.rst +++ b/docs/api/configuration.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.configuration` +:mod:`certbot.configuration` -------------------------------- -.. automodule:: letsencrypt.configuration +.. automodule:: certbot.configuration :members: diff --git a/docs/api/constants.rst b/docs/api/constants.rst index 3a2815b5e..e225056a2 100644 --- a/docs/api/constants.rst +++ b/docs/api/constants.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.constants` +:mod:`certbot.constants` ----------------------------------- -.. automodule:: letsencrypt.constants +.. automodule:: certbot.constants :members: diff --git a/docs/api/continuity_auth.rst b/docs/api/continuity_auth.rst index 82869e6f4..3276220f5 100644 --- a/docs/api/continuity_auth.rst +++ b/docs/api/continuity_auth.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.continuity_auth` +:mod:`certbot.continuity_auth` ---------------------------------- -.. automodule:: letsencrypt.continuity_auth +.. automodule:: certbot.continuity_auth :members: diff --git a/docs/api/crypto_util.rst b/docs/api/crypto_util.rst index 5d4c77538..2f473944c 100644 --- a/docs/api/crypto_util.rst +++ b/docs/api/crypto_util.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.crypto_util` +:mod:`certbot.crypto_util` ------------------------------ -.. automodule:: letsencrypt.crypto_util +.. automodule:: certbot.crypto_util :members: diff --git a/docs/api/display.rst b/docs/api/display.rst index 117a91708..1a18e6534 100644 --- a/docs/api/display.rst +++ b/docs/api/display.rst @@ -1,23 +1,23 @@ -:mod:`letsencrypt.display` +:mod:`certbot.display` -------------------------- -.. automodule:: letsencrypt.display +.. automodule:: certbot.display :members: -:mod:`letsencrypt.display.util` +:mod:`certbot.display.util` =============================== -.. automodule:: letsencrypt.display.util +.. automodule:: certbot.display.util :members: -:mod:`letsencrypt.display.ops` +:mod:`certbot.display.ops` ============================== -.. automodule:: letsencrypt.display.ops +.. automodule:: certbot.display.ops :members: -:mod:`letsencrypt.display.enhancements` +:mod:`certbot.display.enhancements` ======================================= -.. automodule:: letsencrypt.display.enhancements +.. automodule:: certbot.display.enhancements :members: diff --git a/docs/api/errors.rst b/docs/api/errors.rst index 1ad13235c..a9324765b 100644 --- a/docs/api/errors.rst +++ b/docs/api/errors.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.errors` +:mod:`certbot.errors` ------------------------- -.. automodule:: letsencrypt.errors +.. automodule:: certbot.errors :members: diff --git a/docs/api/index.rst b/docs/api/index.rst index a2475eeae..be94214c9 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt` +:mod:`certbot` ------------------ -.. automodule:: letsencrypt +.. automodule:: certbot :members: diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index 00b0a1e50..2988b3b87 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.interfaces` +:mod:`certbot.interfaces` ----------------------------- -.. automodule:: letsencrypt.interfaces +.. automodule:: certbot.interfaces :members: diff --git a/docs/api/le_util.rst b/docs/api/le_util.rst deleted file mode 100644 index 8c6b717cf..000000000 --- a/docs/api/le_util.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.le_util` --------------------------- - -.. automodule:: letsencrypt.le_util - :members: diff --git a/docs/api/log.rst b/docs/api/log.rst index f41c6c4b1..41311de90 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.log` +:mod:`certbot.log` ---------------------- -.. automodule:: letsencrypt.log +.. automodule:: certbot.log :members: diff --git a/docs/api/plugins/common.rst b/docs/api/plugins/common.rst index ca55ba8fb..7cfaf8d70 100644 --- a/docs/api/plugins/common.rst +++ b/docs/api/plugins/common.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.common` +:mod:`certbot.plugins.common` --------------------------------- -.. automodule:: letsencrypt.plugins.common +.. automodule:: certbot.plugins.common :members: diff --git a/docs/api/plugins/disco.rst b/docs/api/plugins/disco.rst index 7bf2b76b4..1a27f0f69 100644 --- a/docs/api/plugins/disco.rst +++ b/docs/api/plugins/disco.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.disco` +:mod:`certbot.plugins.disco` -------------------------------- -.. automodule:: letsencrypt.plugins.disco +.. automodule:: certbot.plugins.disco :members: diff --git a/docs/api/plugins/manual.rst b/docs/api/plugins/manual.rst index 4661ab7df..eea443499 100644 --- a/docs/api/plugins/manual.rst +++ b/docs/api/plugins/manual.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.manual` +:mod:`certbot.plugins.manual` --------------------------------- -.. automodule:: letsencrypt.plugins.manual +.. automodule:: certbot.plugins.manual :members: diff --git a/docs/api/plugins/standalone.rst b/docs/api/plugins/standalone.rst index f5b9d9c24..60aa48b4f 100644 --- a/docs/api/plugins/standalone.rst +++ b/docs/api/plugins/standalone.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.standalone` +:mod:`certbot.plugins.standalone` ------------------------------------- -.. automodule:: letsencrypt.plugins.standalone +.. automodule:: certbot.plugins.standalone :members: diff --git a/docs/api/plugins/util.rst b/docs/api/plugins/util.rst index 6bc8995db..30ab3d49f 100644 --- a/docs/api/plugins/util.rst +++ b/docs/api/plugins/util.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.util` +:mod:`certbot.plugins.util` ------------------------------- -.. automodule:: letsencrypt.plugins.util +.. automodule:: certbot.plugins.util :members: diff --git a/docs/api/plugins/webroot.rst b/docs/api/plugins/webroot.rst index 339d546a5..e1f4523f7 100644 --- a/docs/api/plugins/webroot.rst +++ b/docs/api/plugins/webroot.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.webroot` +:mod:`certbot.plugins.webroot` ---------------------------------- -.. automodule:: letsencrypt.plugins.webroot +.. automodule:: certbot.plugins.webroot :members: diff --git a/docs/api/proof_of_possession.rst b/docs/api/proof_of_possession.rst index db8c6c563..2e7642a45 100644 --- a/docs/api/proof_of_possession.rst +++ b/docs/api/proof_of_possession.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.proof_of_possession` +:mod:`certbot.proof_of_possession` -------------------------------------- -.. automodule:: letsencrypt.proof_of_possession +.. automodule:: certbot.proof_of_possession :members: diff --git a/docs/api/reporter.rst b/docs/api/reporter.rst index 03260f9cd..ad71dbb69 100644 --- a/docs/api/reporter.rst +++ b/docs/api/reporter.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.reporter` +:mod:`certbot.reporter` --------------------------- -.. automodule:: letsencrypt.reporter +.. automodule:: certbot.reporter :members: diff --git a/docs/api/reverter.rst b/docs/api/reverter.rst index 4c220124f..3e0ac750b 100644 --- a/docs/api/reverter.rst +++ b/docs/api/reverter.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.reverter` +:mod:`certbot.reverter` --------------------------- -.. automodule:: letsencrypt.reverter +.. automodule:: certbot.reverter :members: diff --git a/docs/api/storage.rst b/docs/api/storage.rst index 198d85b46..34e3a45c0 100644 --- a/docs/api/storage.rst +++ b/docs/api/storage.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.storage` +:mod:`certbot.storage` -------------------------- -.. automodule:: letsencrypt.storage +.. automodule:: certbot.storage :members: diff --git a/docs/ciphers.rst b/docs/ciphers.rst index ef644b7a0..be6784276 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -17,15 +17,13 @@ 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. +these options are independent of a particular certificate. Certbot +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 +As described below, Certbot 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). +what we think are appropriate defaults when new versions of the Certbot +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. @@ -54,7 +52,7 @@ 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 +step from obtaining a certificate), Certbot 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 @@ -80,30 +78,29 @@ 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. +or a different priority order, than the defaults provided by Certbot. -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. +If you don't use Certbot 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 +Initially, Certbot 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 +and the version implemented by Certbot will be the version that was most current as of the release date of each client version. Mozilla offers three separate sets of cryptographic options, which trade off security and compatibility differently. These are @@ -113,12 +110,12 @@ 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 +the Qualys SSL Labs site, which Certbot 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. +It will be possible to ask Certbot 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 @@ -127,15 +124,15 @@ 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.) +that point, Certbot 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 +team first, before asking the Let's Encrypt project to change +Certbot'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. @@ -144,8 +141,8 @@ 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. +servers configured to track a different expert recommendation, Certbot +could add an option to do so. Resources for recommendations @@ -156,9 +153,9 @@ 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 +https://github.com/certbot/certbot/wiki/Ciphersuite-guidance -Let's Encrypt client users are welcome to review these authorities to +Certbot 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 @@ -172,23 +169,22 @@ This will probably look something like .. code-block:: shell - letsencrypt --cipher-recommendations mozilla-secure - letsencrypt --cipher-recommendations mozilla-intermediate - letsencrypt --cipher-recommendations mozilla-old + certbot --cipher-recommendations mozilla-secure + certbot --cipher-recommendations mozilla-intermediate + certbot --cipher-recommendations mozilla-old to track Mozilla's *Secure*, *Intermediate*, or *Old* recommendations, and .. code-block:: shell - letsencrypt --update-ciphers on + certbot --update-ciphers on -to enable updating ciphers with each new Let's Encrypt client release, -or +to enable updating ciphers with each new Certbot release, or .. code-block:: shell - letsencrypt --update-ciphers off + certbot --update-ciphers off to disable automatic configuration updates. These features have not yet been implemented and this syntax may change then they are implemented. @@ -200,7 +196,7 @@ TODO The status of this feature is tracked as part of issue #1123 in our bug tracker. -https://github.com/letsencrypt/letsencrypt/issues/1123 +https://github.com/certbot/certbot/issues/1123 Prior to implementation of #1123, the client does not actually modify ciphersuites (this is intended to be implemented as a "configuration diff --git a/docs/contributing.rst b/docs/contributing.rst index 69604780c..5a9afd5c5 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -15,14 +15,14 @@ Running a local copy of the client ---------------------------------- 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 +different than running ``certbot-auto``. To get set up, do these things once: .. code-block:: shell - git clone https://github.com/letsencrypt/letsencrypt - cd letsencrypt - ./letsencrypt-auto-source/letsencrypt-auto --os-packages-only + git clone https://github.com/certbot/certbot + cd certbot + ./certbot-auto-source/certbot-auto --os-packages-only ./tools/venv.sh Then in each shell where you're working on the client, do: @@ -36,7 +36,7 @@ client by typing: .. code-block:: shell - letsencrypt + certbot Activating a shell in this way makes it easier to run unit tests with ``tox`` and integration tests, as described below. To reverse this, you @@ -57,8 +57,8 @@ 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 -.. _Good Volunteer Task: https://github.com/letsencrypt/letsencrypt/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 +.. _github issue tracker: https://github.com/certbot/certbot/issues +.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 Testing ------- @@ -97,7 +97,7 @@ Generally it is sufficient to open a pull request and let Github and Travis run integration tests for you. However, if you prefer to run tests, you can use Vagrant, using the Vagrantfile -in Let's Encrypt's repository. To execute the tests on a Vagrant box, the only +in Certbot's repository. To execute the tests on a Vagrant box, the only command you are required to run is:: ./tests/boulder-integration.sh @@ -141,12 +141,12 @@ and ``nginx.wtf`` to 127.0.0.1. You may now run (in a separate terminal):: ./tests/boulder-integration.sh && echo OK || echo FAIL -If you would like to test `letsencrypt_nginx` plugin (highly +If you would like to test `certbot_nginx` plugin (highly encouraged) make sure to install prerequisites as listed in -``letsencrypt-nginx/tests/boulder-integration.sh`` and rerun +``certbot-nginx/tests/boulder-integration.sh`` and rerun the integration tests suite. -.. _Boulder: https://github.com/letsencrypt/boulder +.. _Boulder: https://github.com/certbot/boulder .. _Go: https://golang.org @@ -155,28 +155,28 @@ Code components and layout acme contains all protocol specific code -letsencrypt +certbot all client code Plugin-architecture ------------------- -Let's Encrypt has a plugin architecture to facilitate support for +Certbot has a plugin architecture to facilitate support for different webservers, other TLS servers, and operating systems. The interfaces available for plugins to implement are defined in `interfaces.py`_ and `plugins/common.py`_. The most common kind of plugin is a "Configurator", which is likely to -implement the `~letsencrypt.interfaces.IAuthenticator` and -`~letsencrypt.interfaces.IInstaller` interfaces (though some +implement the `~certbot.interfaces.IAuthenticator` and +`~certbot.interfaces.IInstaller` interfaces (though some Configurators may implement just one of those). -There are also `~letsencrypt.interfaces.IDisplay` plugins, +There are also `~certbot.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/interfaces.py -.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/plugins/common.py#L34 +.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py +.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34 Authenticators @@ -232,7 +232,7 @@ Installer Development --------------------- There are a few existing classes that may be beneficial while -developing a new `~letsencrypt.interfaces.IInstaller`. +developing a new `~certbot.interfaces.IInstaller`. Installers aimed to reconfigure UNIX servers may use Augeas for configuration parsing and can inherit from `~.AugeasConfigurator` class to handle much of the interface. Installers that are unable to use @@ -244,7 +244,7 @@ Display ~~~~~~~ We currently offer a pythondialog and "text" mode for displays. Display -plugins implement the `~letsencrypt.interfaces.IDisplay` +plugins implement the `~certbot.interfaces.IDisplay` interface. .. _dev-plugin: @@ -252,10 +252,10 @@ interface. Writing your own plugin ======================= -Let's Encrypt client supports dynamic discovery of plugins through the +Certbot 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 +custom implementation of `~certbot.interfaces.IAuthenticator` or +the `~certbot.interfaces.IInstaller` without having to merge it with the core upstream source code. An example is provided in ``examples/plugins/`` directory. @@ -323,7 +323,7 @@ Steps: See `Known Issues`_. If it's not a known issue, fix any errors. .. _Known Issues: - https://github.com/letsencrypt/letsencrypt/wiki/Known-issues + https://github.com/certbot/certbot/wiki/Known-issues Updating the documentation ========================== @@ -345,7 +345,7 @@ Other methods for running the client Vagrant ------- -If you are a Vagrant user, Let's Encrypt comes with a Vagrantfile that +If you are a Vagrant user, Certbot 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: @@ -354,7 +354,7 @@ synced to ``/vagrant``, so you can get started with: vagrant ssh cd /vagrant - sudo ./venv/bin/letsencrypt + sudo ./venv/bin/certbot Support for other Linux distributions coming soon. @@ -373,19 +373,19 @@ 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``) +development. Certbot 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 . + docker build -t certbot -f Dockerfile-dev . Now run tests inside the Docker image: .. code-block:: shell - docker run -it letsencrypt bash + docker run -it certbot bash cd src tox -e py27 @@ -399,7 +399,7 @@ OS-level dependencies can be installed like so: .. code-block:: shell - letsencrypt-auto-source/letsencrypt-auto --os-packages-only + certbot-auto-source/certbot-auto --os-packages-only In general... diff --git a/docs/index.rst b/docs/index.rst index 68289d760..b541e376e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,4 @@ -Welcome to the Let's Encrypt client documentation! +Welcome to the Certbot documentation! ================================================== .. toctree:: diff --git a/docs/man/certbot.rst b/docs/man/certbot.rst new file mode 100644 index 000000000..7382d7811 --- /dev/null +++ b/docs/man/certbot.rst @@ -0,0 +1 @@ +.. program-output:: certbot --help all diff --git a/docs/man/letsencrypt.rst b/docs/man/letsencrypt.rst deleted file mode 100644 index 30f33c890..000000000 --- a/docs/man/letsencrypt.rst +++ /dev/null @@ -1 +0,0 @@ -.. program-output:: letsencrypt --help all diff --git a/docs/packaging.rst b/docs/packaging.rst index 5f09b65fa..bd366dbaa 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -3,4 +3,4 @@ Packaging Guide =============== Documentation can be found at -https://github.com/letsencrypt/letsencrypt/wiki/Packaging. +https://github.com/certbot/certbot/wiki/Packaging. diff --git a/docs/using.rst b/docs/using.rst index 66c5907ae..2b16e9a27 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -10,12 +10,12 @@ User Guide Installation ============ -.. _letsencrypt-auto: +.. _certbot-auto: -letsencrypt-auto +certbot-auto ---------------- -``letsencrypt-auto`` is a wrapper which installs some dependencies +``certbot-auto`` is a wrapper which installs some dependencies 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 @@ -25,33 +25,33 @@ To install and run the client, just type... .. code-block:: shell - ./letsencrypt-auto + ./certbot-auto -.. hint:: During the beta phase, Let's Encrypt enforces strict rate limits on +.. hint:: During the beta phase, Certbot enforces strict rate limits on the number of certificates issued for one domain. It is recommended to initially use the test server via `--test-cert` until you get the desired certificates. Throughout the documentation, whenever you see references to -``letsencrypt`` script/binary, you can substitute in -``letsencrypt-auto``. For example, to get basic help you would type: +``certbot`` script/binary, you can substitute in +``certbot-auto``. For example, to get basic help you would type: .. code-block:: shell - ./letsencrypt-auto --help + ./certbot-auto --help or for full help, type: .. code-block:: shell - ./letsencrypt-auto --help all + ./certbot-auto --help all -``letsencrypt-auto`` is the recommended method of running the Let's Encrypt +``certbot-auto`` is the recommended method of running the Certbot client beta releases on systems that don't have a packaged version. Debian, Arch Linux, Gentoo, FreeBSD, and OpenBSD now have native packages, so on those -systems you can just install ``letsencrypt`` (and perhaps -``letsencrypt-apache``). If you'd like to run the latest copy from Git, or +systems you can just install ``certbot`` (and perhaps +``certbot-apache``). If you'd like to run the latest copy from Git, or run your own locally modified copy of the client, follow the instructions in the :doc:`contributing`. Some `other methods of installation`_ are discussed below. @@ -60,11 +60,11 @@ below. Plugins ======= -The Let's Encrypt client supports a number of different "plugins" that can be +The Certbot client supports a number of different "plugins" that can be used to obtain and/or install certificates. Plugins that can obtain a cert are called "authenticators" and can be used with the "certonly" command. Plugins that can install a cert are called "installers". Plugins that do both -can be used with the "letsencrypt run" command, which is the default. +can be used with the "certbot run" command, which is the default. =========== ==== ==== =============================================================== Plugin Auth Inst Notes @@ -79,7 +79,7 @@ standalone_ Y N Uses a "standalone" webserver to obtain a cert. Requires webserver is not supported or not desired. manual_ Y N Helps you obtain a cert by giving you instructions to perform domain validation yourself. -nginx_ Y Y Very experimental and not included in letsencrypt-auto_. +nginx_ Y Y Very experimental and not included in certbot-auto_. =========== ==== ==== =============================================================== There are also a number of third-party plugins for the client, provided by other developers: @@ -93,10 +93,10 @@ s3front_ Y Y Integration with Amazon CloudFront distribution of S3 buck gandi_ Y Y Integration with Gandi's hosting products and API =========== ==== ==== =============================================================== -.. _plesk: https://github.com/plesk/letsencrypt-plesk -.. _haproxy: https://code.greenhost.net/open/letsencrypt-haproxy -.. _s3front: https://github.com/dlapiduz/letsencrypt-s3front -.. _gandi: https://github.com/Gandi/letsencrypt-gandi +.. _plesk: https://github.com/plesk/certbot-plesk +.. _haproxy: https://code.greenhost.net/open/certbot-haproxy +.. _s3front: https://github.com/dlapiduz/certbot-s3front +.. _gandi: https://github.com/Gandi/certbot-gandi Future plugins for IMAP servers, SMTP servers, IRC servers, etc, are likely to be installers but not authenticators. @@ -130,21 +130,21 @@ specified ``--webroot-path``. So, for instance, :: - letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net + certbot certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net would obtain a single certificate for all of those names, using the ``/var/www/example`` webroot directory for the first two, and ``/var/www/other`` for the second two. The webroot plugin works by creating a temporary file for each of your requested -domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's -Encrypt validation server makes HTTP requests to validate that the DNS for each -requested domain resolves to the server running letsencrypt. An example request +domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Certbot +validation server makes HTTP requests to validate that the DNS for each +requested domain resolves to the server running certbot. An example request made to your web server would look like: :: - 66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" + 66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Certbot validation server; +https://www.certbot.com)" Note that to use the webroot plugin, your server must be configured to serve files from hidden directories. If ``/.well-known`` is treated specially by @@ -173,7 +173,7 @@ specified port using each requested domain name. Manual ------ -If you'd like to obtain a cert running ``letsencrypt`` on a machine +If you'd like to obtain a cert running ``certbot`` on a machine other than your target webserver or perform the steps for domain validation yourself, you can use the manual plugin. While hidden from the UI, you can use the plugin to obtain a cert by specifying @@ -187,14 +187,14 @@ Nginx In the future, if you're running Nginx you can use this plugin to automatically obtain and install your certificate. The Nginx plugin is still experimental, however, and is not installed with -letsencrypt-auto_. If installed, you can select this plugin on the +certbot-auto_. If installed, you can select this plugin on the command line by including ``--nginx``. Third-party plugins ------------------- These plugins are listed at -https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If you're +https://github.com/certbot/certbot/wiki/Plugins. If you're interested, you can also :ref:`write your own plugin `. Renewal @@ -204,11 +204,11 @@ Renewal days). Make sure you renew the certificates at least once in 3 months. -The ``letsencrypt`` client now supports a ``renew`` action to check +The ``certbot`` client now supports a ``renew`` action to check all installed certificates for impending expiry and attempt to renew them. The simplest form is simply -``letsencrypt renew`` +``certbot renew`` This will attempt to renew any previously-obtained certificates that expire in less than 30 days. The same plugin and options that were used @@ -229,9 +229,9 @@ certificate regardless of its age. (This form is not appropriate to run daily because each certificate will be renewed every day, which will quickly run into the certificate authority rate limit.) -Note that options provided to ``letsencrypt renew`` will apply to +Note that options provided to ``certbot renew`` will apply to *every* certificate for which renewal is attempted; for example, -``letsencrypt renew --rsa-key-size 4096`` would try to replace every +``certbot renew --rsa-key-size 4096`` would try to replace every near-expiry certificate with an equivalent certificate using a 4096-bit RSA public key. If a certificate is successfully renewed using specified options, those options will be saved and used for future @@ -240,10 +240,10 @@ renewals of that certificate. An alternative form that provides for more fine-grained control over the renewal process (while renewing specified certificates one at a time), -is ``letsencrypt certonly`` with the complete set of subject domains of +is ``certbot certonly`` with the complete set of subject domains of a specific certificate specified via `-d` flags, like -``letsencrypt certonly -d example.com -d www.example.com`` +``certbot certonly -d example.com -d www.example.com`` (All of the domains covered by the certificate must be specified in this case in order to renew and replace the old certificate rather @@ -256,7 +256,7 @@ The ``certonly`` form attempts to renew one individual certificate. 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 improving the renewal process, and we +Certbot is working hard on improving the renewal process, and we apologize for any inconveniences you encounter in integrating these commands into your individual environment. @@ -272,14 +272,14 @@ 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 +``/etc/certbot/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 +symlinks). During the renewal_, ``/etc/certbot/live`` is updated with the latest necessary files. -.. note:: ``/etc/letsencrypt/archive`` and ``/etc/letsencrypt/keys`` +.. note:: ``/etc/certbot/archive`` and ``/etc/certbot/keys`` contain all previous keys and certificates, while - ``/etc/letsencrypt/live`` symlinks to the latest versions. + ``/etc/certbot/live`` symlinks to the latest versions. The following files are available: @@ -287,7 +287,7 @@ The following files are available: 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 + it with anyone, including Certbot developers. You cannot put it into a safe, however - your server still needs to access this file in order for SSL/TLS to work. @@ -340,7 +340,7 @@ Configuration file ================== It is possible to specify configuration file with -``letsencrypt-auto --config cli.ini`` (or shorter ``-c cli.ini``). An +``certbot-auto --config cli.ini`` (or shorter ``-c cli.ini``). An example configuration file is shown below: .. include:: ../examples/cli.ini @@ -348,9 +348,9 @@ example configuration file is shown below: By default, the following locations are searched: -- ``/etc/letsencrypt/cli.ini`` -- ``$XDG_CONFIG_HOME/letsencrypt/cli.ini`` (or - ``~/.config/letsencrypt/cli.ini`` if ``$XDG_CONFIG_HOME`` is not +- ``/etc/certbot/cli.ini`` +- ``$XDG_CONFIG_HOME/certbot/cli.ini`` (or + ``~/.config/certbot/cli.ini`` if ``$XDG_CONFIG_HOME`` is not set). .. keep it up to date with constants.py @@ -359,21 +359,21 @@ By default, the following locations are searched: Getting help ============ -If you're having problems you can chat with us on `IRC (#letsencrypt @ -Freenode) `_ or -get support on our `forums `_. +If you're having problems you can chat with us on `IRC (#certbot @ +OFTC) `_ or +get support on our `forums `_. If you find a bug in the software, please do report it in our `issue tracker -`_. Remember to +`_. Remember to give 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 +- copy and paste logs from ``/var/log/certbot`` (though mind they also might contain personally identifiable information) -- copy and paste ``letsencrypt --version`` output +- copy and paste ``certbot --version`` output - your operating system, including specific version - specify which installation_ method you've chosen @@ -390,10 +390,10 @@ 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 +manually. https://github.com/certbot/certbot/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 +certbot-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, @@ -402,14 +402,14 @@ to, `install Docker`_, then issue the following command: .. code-block:: shell - 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 + sudo docker run -it --rm -p 443:443 -p 80:80 --name certbot \ + -v "/etc/certbot:/etc/certbot" \ + -v "/var/lib/certbot:/var/lib/certbot" \ + quay.io/certbot/certbot:latest auth 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. +in ``/etc/certbot/live`` on the host. .. _Docker: https://docker.com .. _`install Docker`: https://docs.docker.com/userguide/ @@ -420,31 +420,31 @@ Operating System Packages **FreeBSD** - * Port: ``cd /usr/ports/security/py-letsencrypt && make install clean`` - * Package: ``pkg install py27-letsencrypt`` + * Port: ``cd /usr/ports/security/py-certbot make install clean`` + * Package: ``pkg install py27-certbot`` **OpenBSD** - * Port: ``cd /usr/ports/security/letsencrypt/client && make install clean`` - * Package: ``pkg_add letsencrypt`` + * Port: ``cd /usr/ports/security/certbot/client && make install clean`` + * Package: ``pkg_add certbot`` **Arch Linux** .. code-block:: shell - sudo pacman -S letsencrypt letsencrypt-apache + sudo pacman -S certbot certbot-apache **Debian** -If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. +If you run Debian Stretch or Debian Sid, you can install certbot packages. .. code-block:: shell sudo apt-get update - sudo apt-get install letsencrypt python-letsencrypt-apache + sudo apt-get install certbot python-certbot-apache If you don't want to use the Apache plugin, you can omit the -``python-letsencrypt-apache`` package. +``python-certbot-apache`` package. Packages for Debian Jessie are coming in the next few weeks. @@ -452,17 +452,17 @@ Packages for Debian Jessie are coming in the next few weeks. .. code-block:: shell - sudo dnf install letsencrypt + sudo dnf install certbot **Gentoo** -The official Let's Encrypt client is available in Gentoo Portage. If you +The official Certbot client is available in Gentoo Portage. If you want to use the Apache plugin, it has to be installed separately: .. code-block:: shell - emerge -av app-crypt/letsencrypt - emerge -av app-crypt/letsencrypt-apache + emerge -av app-crypt/certbot + emerge -av app-crypt/certbot-apache Currently, only the Apache plugin is included in Portage. However, if you want the nginx plugin, you can use Layman to add the mrueg overlay which @@ -473,7 +473,7 @@ does include the nginx plugin package: emerge -av app-portage/layman layman -S layman -a mrueg - emerge -av app-crypt/letsencrypt-nginx + emerge -av app-crypt/certbot-nginx When using the Apache plugin, you will run into a "cannot find a cert or key directive" error if you're sporting the default Gentoo ``httpd.conf``. @@ -503,7 +503,7 @@ Note: this change is not required for the other plugins. **Other Operating Systems** OS packaging is an ongoing effort. If you'd like to package -Let's Encrypt client for your distribution of choice please have a +Certbot for your distribution of choice please have a look at the :doc:`packaging`. @@ -519,19 +519,19 @@ whole process is described in the :doc:`contributing`. 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! + Certbot 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 +the certbot-auto_ method. It's the fastest, the most thoroughly tested and the most reliable way of getting our software and the free SSL certificates! Beyond the methods discussed here, other methods may be possible, such as -installing Let's Encrypt directly with pip from PyPI or downloading a ZIP +installing Certbot directly with pip from PyPI or downloading a ZIP archive from GitHub may be technically possible but are not presently recommended or supported. From 1f4daf0874a038ee3bd4c832309492171c1566c1 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 16:42:45 -0700 Subject: [PATCH 133/317] factor out _create_challenge_dirs --- letsencrypt/plugins/webroot.py | 9 +++++---- letsencrypt/plugins/webroot_test.py | 29 ++++++++--------------------- letsencrypt/tests/cli_test.py | 7 ------- 3 files changed, 13 insertions(+), 32 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 7c9aef509..4b13b05bc 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -67,6 +67,11 @@ to serve all files under specified web root ({0}).""" if self.conf("path"): _match_webroot_with_domains(self.config) + def perform(self, achalls): # pylint: disable=missing-docstring + self._create_challenge_dirs() + return [self._perform_single(achall) for achall in achalls] + + def _create_challenge_dirs(self): path_map = self.conf("map") if not path_map: raise errors.PluginError( @@ -111,10 +116,6 @@ to serve all files under specified web root ({0}).""" finally: os.umask(old_umask) - def perform(self, achalls): # pylint: disable=missing-docstring - assert self.full_roots, "Webroot plugin appears to be missing webroot map" - return [self._perform_single(achall) for achall in achalls] - def _get_root_path(self, achall): try: path = self.full_roots[achall.domain] diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 23aabd82d..0c7a2a3b0 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -55,14 +55,13 @@ class AuthenticatorTest(unittest.TestCase): self.auth.add_parser_arguments(add) self.assertEqual(2, add.call_count) - def test_prepare_missing_root(self): + def test_prepare(self): + self.auth.prepare() # shouldn't raise any exceptions + + def test_perform_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} - self.assertRaises(errors.PluginError, self.auth.prepare) - - def test_prepare_full_root_exists(self): - # prepare() has already been called once in setUp() - self.auth.prepare() # shouldn't raise any exceptions + self.assertRaises(errors.PluginError, self.auth.perform, []) def test_prepare_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") @@ -75,7 +74,7 @@ class AuthenticatorTest(unittest.TestCase): print("Warning, running tests as root skips permissions tests...") except IOError: # ok, permissions work, test away... - self.assertRaises(errors.PluginError, self.auth.prepare) + self.assertRaises(errors.PluginError, self.auth.perform, []) os.chmod(self.path, 0o700) @mock.patch("letsencrypt.plugins.webroot.os.chown") @@ -86,9 +85,9 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_not_eacces(self, mock_chown): mock_chown.side_effect = OSError() - self.assertRaises(errors.PluginError, self.auth.prepare) + self.assertRaises(errors.PluginError, self.auth.perform, []) - def test_prepare_permissions(self): + def test_perform_permissions(self): self.auth.prepare() # Remove exec bit from permission check, so that it @@ -111,18 +110,6 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(os.stat(self.validation_path).st_gid, parent_gid) self.assertEqual(os.stat(self.validation_path).st_uid, parent_uid) - def test_perform_missing_path(self): - self.auth.prepare() - - missing_achall = achallenges.KeyAuthorizationAnnotatedChallenge( - challb=acme_util.HTTP01_P, domain="thing2.com", account_key=KEY) - self.assertRaises( - errors.PluginError, self.auth.perform, [missing_achall]) - - self.auth.full_roots[self.achall.domain] = 'null' - self.assertRaises( - errors.PluginError, self.auth.perform, [self.achall]) - def test_perform_cleanup(self): self.auth.prepare() responses = self.auth.perform([self.achall]) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 71c6356bb..6182d99cc 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -234,13 +234,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue("The nginx plugin is not working" in ret) self.assertTrue("MisconfigurationError" in ret) - args = ["certonly", "--webroot"] - try: - self._call(args) - assert False, "Exception should have been raised" - except errors.PluginSelectionError as e: - self.assertTrue("please set either --webroot-path" in e.message) - self._cli_missing_flag(["--standalone"], "With the standalone plugin, you probably") with mock.patch("letsencrypt.main._init_le_client") as mock_init: From 7070b996995b8b26469a8a68a2bc0f9a7f0beca0 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 16:45:59 -0700 Subject: [PATCH 134/317] make prepare a noop --- letsencrypt/plugins/webroot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 4b13b05bc..5f3c04448 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -64,8 +64,7 @@ to serve all files under specified web root ({0}).""" self.performed = defaultdict(set) def prepare(self): # pylint: disable=missing-docstring - if self.conf("path"): - _match_webroot_with_domains(self.config) + pass def perform(self, achalls): # pylint: disable=missing-docstring self._create_challenge_dirs() From 82efffdf62e3834f0e5f24846f2c839582906524 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 16:52:48 -0700 Subject: [PATCH 135/317] inline _match_webroot_with_domains --- letsencrypt/plugins/webroot.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 5f3c04448..57bccb13c 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -201,7 +201,9 @@ class _WebrootPathAction(argparse.Action): if namespace.webroot_path: # Apply previous webroot to all matched # domains before setting the new webroot path - _match_webroot_with_domains(namespace) + prev_webroot = namespace.webroot_path[-1] + for domain in namespace.domains: + namespace.webroot_map.setdefault(domain, prev_webroot) elif namespace.domains: self._domain_before_webroot = True @@ -221,16 +223,3 @@ def _validate_webroot(webroot_path): raise errors.PluginError(webroot_path + " does not exist or is not a directory") return os.path.abspath(webroot_path) - - -def _match_webroot_with_domains(args_or_config): - """Applies the most recent webroot path to all unmatched domains. - - :param args_or_config: parsed command line arguments - :type args_or_config: argparse.Namespace or - configuration.NamespaceConfig - - """ - webroot_path = args_or_config.webroot_path[-1] - for domain in args_or_config.domains: - args_or_config.webroot_map.setdefault(domain, webroot_path) From 1acd50a0ceb93032170973c8a52cca2e1881adf2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 17:07:54 -0700 Subject: [PATCH 136/317] Remove the need for extra processing to support --csr + --webroot --- letsencrypt/cli.py | 14 ++++---------- letsencrypt/plugins/webroot.py | 6 ++++++ letsencrypt/tests/client_test.py | 6 +----- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 5e192424a..edb97ae1c 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -302,10 +302,7 @@ class HelpfulArgumentParser(object): return parsed_args def handle_csr(self, parsed_args): - """ - Process a --csr flag. This needs to happen early enough that the - webroot plugin can know about the calls to add_domains. - """ + """Process a --csr flag.""" if parsed_args.verb != "certonly": raise errors.Error("Currently, a CSR file may only be specified " "when obtaining a new or replacement " @@ -327,9 +324,6 @@ class HelpfulArgumentParser(object): logger.debug("PEM CSR parse error %s", traceback.format_exc()) raise errors.Error("Failed to parse CSR file: {0}".format(parsed_args.csr[0])) - for d in domains: - add_domains(parsed_args, d) - if not domains: # TODO: add CN to domains instead: raise errors.Error( @@ -337,11 +331,11 @@ class HelpfulArgumentParser(object): % parsed_args.csr[0]) parsed_args.actual_csr = (csr, typ) - csr_domains, config_domains = set(domains), set(parsed_args.domains) - if csr_domains != config_domains: + # If CSR domains are not a superset of the domains provided by the CLI + if set(parsed_args.domains) - set(domains): raise errors.ConfigurationError( "Inconsistent domain requests:\nFrom the CSR: {0}\nFrom command line/config: {1}" - .format(", ".join(csr_domains), ", ".join(config_domains))) + .format(", ".join(domains), ", ".join(parsed_args.domains))) def determine_verb(self): diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 57bccb13c..b6483b228 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -67,7 +67,13 @@ to serve all files under specified web root ({0}).""" pass def perform(self, achalls): # pylint: disable=missing-docstring + if self.conf("path"): + webroot_path = self.conf("path")[-1] + for achall in achalls: + self.conf("map").setdefault(achall.domain, webroot_path) + self._create_challenge_dirs() + return [self._perform_single(achall) for achall in achalls] def _create_challenge_dirs(self): diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index 00e03e7e4..8943ffa00 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -135,8 +135,7 @@ class ClientTest(unittest.TestCase): # FIXME move parts of this to test_cli.py... @mock.patch("letsencrypt.client.logger") - @mock.patch("letsencrypt.cli.add_domains") - def test_obtain_certificate_from_csr(self, mock_add_domains, mock_logger): + def test_obtain_certificate_from_csr(self, mock_logger): self._mock_obtain_certificate() from letsencrypt import cli test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN) @@ -150,9 +149,6 @@ class ClientTest(unittest.TestCase): mock_parser = mock.MagicMock(cli.HelpfulArgumentParser) cli.HelpfulArgumentParser.handle_csr(mock_parser, mock_parsed_args) - # make sure cli processing occurred - cli_processed = (call[0][1] for call in mock_add_domains.call_args_list) - self.assertEqual(set(cli_processed), set(("example.com", "www.example.com"))) # Now provoke an inconsistent domains error... mock_parsed_args.domains.append("hippopotamus.io") self.assertRaises(errors.ConfigurationError, From 547147b8ac0b709f11f53c558a323e91a145beef Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 17:57:46 -0700 Subject: [PATCH 137/317] Start of IDisplay code for webroot --- letsencrypt/plugins/webroot.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index b6483b228..428469383 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -1,13 +1,15 @@ """Webroot plugin.""" import argparse +import collections import errno +import itertools import json import logging import os -from collections import defaultdict -import zope.interface import six +import zope.component +import zope.interface from acme import challenges @@ -61,21 +63,39 @@ to serve all files under specified web root ({0}).""" def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) self.full_roots = {} - self.performed = defaultdict(set) + self.performed = collections.defaultdict(set) def prepare(self): # pylint: disable=missing-docstring pass def perform(self, achalls): # pylint: disable=missing-docstring - if self.conf("path"): - webroot_path = self.conf("path")[-1] - for achall in achalls: - self.conf("map").setdefault(achall.domain, webroot_path) + self._get_webroots(achalls) self._create_challenge_dirs() return [self._perform_single(achall) for achall in achalls] + def _get_webroots(self, achalls): + if self.conf("path"): + webroot_path = self.conf("path")[-1] + for achall in achalls: + self.conf("map").setdefault(achall.domain, webroot_path) + else: + # An OrderedDict is used because it maintains + # insertion order and fast element lookup + known_webroots = collections.OrderedDict( + (path, None) for path in six.itervalues(self.conf("map"))) + for achall in achalls: + if achall.domain not in self.conf("map"): + self._prompt_for_webroot(achall.domain, known_webroots) + + def _prompt_for_webroot(self, domain, known_webroots): + display = zope.component.getUtility(interfaces.IDisplay) + display.menu( + "Select the webroot for {0}:".format(domain), + itertools.chain(("Enter a new webroot",), known_webroots), + help_label="Help") + def _create_challenge_dirs(self): path_map = self.conf("map") if not path_map: From f1f0d1de12184de179a475bee476b2095cd9420a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 18:08:27 -0700 Subject: [PATCH 138/317] premature optimization is the root of all evil --- letsencrypt/plugins/webroot.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 428469383..4e83d04ea 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -2,7 +2,6 @@ import argparse import collections import errno -import itertools import json import logging import os @@ -81,10 +80,7 @@ to serve all files under specified web root ({0}).""" for achall in achalls: self.conf("map").setdefault(achall.domain, webroot_path) else: - # An OrderedDict is used because it maintains - # insertion order and fast element lookup - known_webroots = collections.OrderedDict( - (path, None) for path in six.itervalues(self.conf("map"))) + known_webroots = list(six.itervalues(self.conf("map"))) for achall in achalls: if achall.domain not in self.conf("map"): self._prompt_for_webroot(achall.domain, known_webroots) @@ -93,7 +89,7 @@ to serve all files under specified web root ({0}).""" display = zope.component.getUtility(interfaces.IDisplay) display.menu( "Select the webroot for {0}:".format(domain), - itertools.chain(("Enter a new webroot",), known_webroots), + ["Enter a new webroot"] + known_webroots, help_label="Help") def _create_challenge_dirs(self): From 33a4acf6eb99f8d9639ad3fea3ecf7ed4494419f Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Fri, 1 Apr 2016 18:09:20 -0700 Subject: [PATCH 139/317] Explicit test for modified write_renewal_config --- letsencrypt/tests/storage_test.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 7e862146d..5ea455496 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -742,6 +742,29 @@ class RenewableCertTests(BaseRenewableCertTest): storage.RenewableCert, self.config.filename, self.cli_config) + def test_write_renewal_config(self): + # Mostly tested by the process of creating and updating lineages, + # but we can test that this successfully creates files, removes + # unneeded items, and preserves comments. + tempfile = os.path.join(self.tempdir, "sample-file") + tempfile2 = os.path.join(self.tempdir, "sample-file.new") + with open(tempfile, "w") as f: + f.write("[renewalparams]\nuseful = value # A useful value\n" + "useless = value # Not needed\n") + target = {} + for x in ALL_FOUR: + target[x] = "somewhere" + relevant = {"useful": "new_value"} + from letsencrypt import storage + storage.write_renewal_config(tempfile, tempfile2, target, relevant) + with open(tempfile2, "r") as f: + content = f.read() + # useful value was updated + assert "useful = new_value" in content + # associated comment was preserved + assert "A useful value" in content + # useless value was deleted + assert "useless" not in content if __name__ == "__main__": unittest.main() # pragma: no cover From 13a5faeda7728e12869860f94dc0ac9c9df46824 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 18:14:50 -0700 Subject: [PATCH 140/317] An attempt at --quiet --- letsencrypt/cli.py | 17 ++++++++----- letsencrypt/main.py | 15 ++++++----- letsencrypt/renewal.py | 58 +++++++++++++++++++++++++----------------- 3 files changed, 54 insertions(+), 36 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 256e0c801..7ff47990f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -600,6 +600,13 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "regardless of whether it is near expiry. (Often " "--keep-until-expiring is more appropriate). Also implies " "--expand.") + helpful.add( + "automation", "--allow-subset-of-names", action="store_true", + help="When performing domain validation, do not consider it a failure " + "if authorizations can not be obtained for a strict subset of " + "the requested domains. This may be useful for allowing renewals for " + "multiple domains to succeed even if some domains no longer point " + "at this system. This option cannot be used with --csr.") helpful.add( "automation", "--agree-tos", dest="tos", action="store_true", help="Agree to the Let's Encrypt Subscriber Agreement") @@ -617,6 +624,10 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "automation", "--no-self-upgrade", action="store_true", help="(letsencrypt-auto only) prevent the letsencrypt-auto script from" " upgrading itself to newer released versions") + helpful.add( + "automation", "-q", "--quiet", dest="quiet", action="store_true", + help="Silence all output except errors. Useful for automation via cron." + "Implies --non-interactive.") helpful.add_group( "testing", description="The following flags are meant for " @@ -677,12 +688,6 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "security", "--strict-permissions", action="store_true", help="Require that all configuration files are owned by the current " "user; only needed if your config is somewhere unsafe like /tmp/") - helpful.add( - "automation", "--allow-subset-of-names", - action="store_true", - help="When performing domain validation, do not consider it a failure " - "if authorizations can not be obtained for a strict subset of " - "the requested domains. This option cannot be used with --csr.") helpful.add_group( "renew", description="The 'renew' subcommand will attempt to renew all" diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 0afccc85e..1998e09ea 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -34,7 +34,6 @@ from letsencrypt.display import util as display_util, ops as display_ops from letsencrypt.plugins import disco as plugins_disco from letsencrypt.plugins import selection as plug_sel - logger = logging.getLogger(__name__) @@ -537,20 +536,21 @@ def obtain_cert(config, plugins, lineage=None): domains = _find_domains(config, installer) _, action = _auth_from_domains(le_client, config, domains, lineage) + notify = zope.component.getUtility(interfaces.IDisplay).notification if config.dry_run: _report_successful_dry_run(config) elif config.verb == "renew": if installer is None: # Tell the user that the server was not restarted. - print("new certificate deployed without reload, fullchain is", - lineage.fullchain) + notify("new certificate deployed without reload, fullchain is {0}".format( + lineage.fullchain), pause=False) else: # In case of a renewal, reload server to pick up new certificate. # In principle we could have a configuration option to inhibit this # from happening. installer.restart() - print("new certificate deployed with reload of", - config.installer, "server; fullchain is", lineage.fullchain) + notify("new certificate deployed with reload of {0} server; fullchain is {1}".format( + config.installer, lineage.fullchain), pause=False) _suggest_donation_if_appropriate(config, action) def renew(config, unused_plugins): @@ -689,7 +689,10 @@ def main(cli_args=sys.argv[1:]): sys.excepthook = functools.partial(_handle_exception, config=config) # Displayer - if config.noninteractive_mode: + if config.quiet: + config.noninteractive_mode = True + displayer = display_util.NoninteractiveDisplay(open("/dev/null", "w")) + elif config.noninteractive_mode: displayer = display_util.NoninteractiveDisplay(sys.stdout) elif config.text_mode: displayer = display_util.FileDisplay(sys.stdout) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 27546bec9..f0edd55fe 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -12,6 +12,7 @@ import zope.component from letsencrypt import configuration from letsencrypt import cli from letsencrypt import errors +from letsencrypt import interfaces from letsencrypt import storage from letsencrypt.plugins import disco as plugins_disco @@ -201,44 +202,53 @@ def should_renew(config, lineage): def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): - def _status(msgs, category): - return " " + "\n ".join("%s (%s)" % (m, category) for m in msgs) + def notify(msg): + "Our version of print()" + zope.component.getUtility(interfaces.IDisplay).notification(msg, pause=False) + + def report(msgs, category): + "Report results for a category of renewal outcomes" + notify(" " + "\n ".join("%s (%s)" % (m, category) for m in msgs)) + if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates below have not been saved.)") - print() + notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + notify("** (The test certificates below have not been saved.)") + notify("") if renew_skipped: - print("The following certs are not due for renewal yet:") - print(_status(renew_skipped, "skipped")) + notify("The following certs are not due for renewal yet:") + report(renew_skipped, "skipped") if not renew_successes and not renew_failures: - print("No renewals were attempted.") + notify("No renewals were attempted.") elif renew_successes and not renew_failures: - print("Congratulations, all renewals succeeded. The following certs " - "have been renewed:") - print(_status(renew_successes, "success")) + notify("Congratulations, all renewals succeeded. The following certs " + "have been renewed:") + report(renew_successes, "success") elif renew_failures and not renew_successes: - print("All renewal attempts failed. The following certs could not be " - "renewed:") - print(_status(renew_failures, "failure")) + notify("All renewal attempts failed. The following certs could not be " + "renewed:") + report(renew_failures, "failure") elif renew_failures and renew_successes: - print("The following certs were successfully renewed:") - print(_status(renew_successes, "success")) - print("\nThe following certs could not be renewed:") - print(_status(renew_failures, "failure")) + notify("The following certs were successfully renewed:") + report(renew_successes, "success") + notify("\nThe following certs could not be renewed:") + report(renew_failures, "failure") if parse_failures: - print("\nAdditionally, the following renewal configuration files " - "were invalid: ") - print(_status(parse_failures, "parsefail")) + notify("\nAdditionally, the following renewal configuration files " + "were invalid: ") + report(parse_failures, "parsefail") if config.dry_run: - print("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") - print("** (The test certificates above have not been saved.)") + notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + notify("** (The test certificates above have not been saved.)") def renew_all_lineages(config): """Examine each lineage; renew if due and report results""" + def _notify(msg): + zope.component.getUtility(interfaces.IDisplay).notification(msg, pause=False) + if config.domains != []: raise errors.Error("Currently, the renew verb is only capable of " "renewing all installed certificates that are due " @@ -253,7 +263,7 @@ def renew_all_lineages(config): renew_skipped = [] parse_failures = [] for renewal_file in renewal_conf_files(renewer_config): - print("Processing " + renewal_file) + _notify("Processing " + renewal_file) lineage_config = copy.deepcopy(config) # Note that this modifies config (to add back the configuration From dd14b9980e7e173729ce7c040f0293fd1dbc9ce3 Mon Sep 17 00:00:00 2001 From: Seth Schoen Date: Fri, 1 Apr 2016 18:21:16 -0700 Subject: [PATCH 141/317] Don't call our tempfile "tempfile" --- letsencrypt/tests/storage_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/letsencrypt/tests/storage_test.py b/letsencrypt/tests/storage_test.py index 5ea455496..fcc481e8b 100644 --- a/letsencrypt/tests/storage_test.py +++ b/letsencrypt/tests/storage_test.py @@ -746,18 +746,18 @@ class RenewableCertTests(BaseRenewableCertTest): # Mostly tested by the process of creating and updating lineages, # but we can test that this successfully creates files, removes # unneeded items, and preserves comments. - tempfile = os.path.join(self.tempdir, "sample-file") - tempfile2 = os.path.join(self.tempdir, "sample-file.new") - with open(tempfile, "w") as f: + temp = os.path.join(self.tempdir, "sample-file") + temp2 = os.path.join(self.tempdir, "sample-file.new") + with open(temp, "w") as f: f.write("[renewalparams]\nuseful = value # A useful value\n" "useless = value # Not needed\n") target = {} for x in ALL_FOUR: target[x] = "somewhere" - relevant = {"useful": "new_value"} + relevant_data = {"useful": "new_value"} from letsencrypt import storage - storage.write_renewal_config(tempfile, tempfile2, target, relevant) - with open(tempfile2, "r") as f: + storage.write_renewal_config(temp, temp2, target, relevant_data) + with open(temp2, "r") as f: content = f.read() # useful value was updated assert "useful = new_value" in content From 1f4f1fdf39e83757d53577e3203f478446fac76d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 18:22:07 -0700 Subject: [PATCH 142/317] Verbosity tuning --- letsencrypt/renewal.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index f0edd55fe..af4bbca1e 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -203,12 +203,12 @@ def should_renew(config, lineage): def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): def notify(msg): - "Our version of print()" + "A variant of print() that is silenced by -q" zope.component.getUtility(interfaces.IDisplay).notification(msg, pause=False) def report(msgs, category): "Report results for a category of renewal outcomes" - notify(" " + "\n ".join("%s (%s)" % (m, category) for m in msgs)) + return " " + "\n ".join("%s (%s)" % (m, category) for m in msgs) if config.dry_run: notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") @@ -216,27 +216,27 @@ def _renew_describe_results(config, renew_successes, renew_failures, notify("") if renew_skipped: notify("The following certs are not due for renewal yet:") - report(renew_skipped, "skipped") + notify(report(renew_skipped, "skipped")) if not renew_successes and not renew_failures: notify("No renewals were attempted.") elif renew_successes and not renew_failures: notify("Congratulations, all renewals succeeded. The following certs " "have been renewed:") - report(renew_successes, "success") + notify(report(renew_successes, "success")) elif renew_failures and not renew_successes: - notify("All renewal attempts failed. The following certs could not be " + logger.error("All renewal attempts failed. The following certs could not be " "renewed:") - report(renew_failures, "failure") + logger.error(report(renew_failures, "failure")) elif renew_failures and renew_successes: - notify("The following certs were successfully renewed:") - report(renew_successes, "success") - notify("\nThe following certs could not be renewed:") - report(renew_failures, "failure") + logger.error("The following certs were successfully renewed:") + logger.error(report(renew_successes, "success")) + logger.error("\nThe following certs could not be renewed:") + logger.error(renew_failures, "failure") if parse_failures: - notify("\nAdditionally, the following renewal configuration files " - "were invalid: ") - report(parse_failures, "parsefail") + logger.error("\nAdditionally, the following renewal configuration files " + "were invalid: ") + logger.error(parse_failures, "parsefail") if config.dry_run: notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") From 10214763b0f2a4573e07a21d0321472593a61552 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 18:23:38 -0700 Subject: [PATCH 143/317] bugfix --- letsencrypt/renewal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index af4bbca1e..afca174a7 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -231,7 +231,7 @@ def _renew_describe_results(config, renew_successes, renew_failures, logger.error("The following certs were successfully renewed:") logger.error(report(renew_successes, "success")) logger.error("\nThe following certs could not be renewed:") - logger.error(renew_failures, "failure") + logger.error(report(renew_failures, "failure")) if parse_failures: logger.error("\nAdditionally, the following renewal configuration files " From ba62ed45c0b0fc5a0b4f5d42f2b4703d45c95ab1 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 18:39:13 -0700 Subject: [PATCH 144/317] basic interactive webroot? --- letsencrypt/plugins/webroot.py | 37 +++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 4e83d04ea..ac4c3c1ec 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -15,6 +15,7 @@ from acme import challenges from letsencrypt import cli from letsencrypt import errors from letsencrypt import interfaces +from letsencrypt.display import util as display_util from letsencrypt.plugins import common @@ -34,6 +35,9 @@ 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}).""" + _INTERACTIVE_CANCEL = ("Every requested domain must have a " + "webroot when using the webroot plugin.") + def more_info(self): # pylint: disable=missing-docstring,no-self-use return self.MORE_INFO.format(self.conf("path")) @@ -80,17 +84,40 @@ to serve all files under specified web root ({0}).""" for achall in achalls: self.conf("map").setdefault(achall.domain, webroot_path) else: - known_webroots = list(six.itervalues(self.conf("map"))) + known_webroots = list(set(six.itervalues(self.conf("map")))) for achall in achalls: if achall.domain not in self.conf("map"): - self._prompt_for_webroot(achall.domain, known_webroots) + new_webroot = self._prompt_for_webroot(achall.domain, + known_webroots) + try: + known_webroots.remove(new_webroot) + except ValueError: + pass + known_webroots.append(new_webroot) + self.conf("map")[achall.domain] = new_webroot def _prompt_for_webroot(self, domain, known_webroots): display = zope.component.getUtility(interfaces.IDisplay) - display.menu( + code, index = display.menu( "Select the webroot for {0}:".format(domain), - ["Enter a new webroot"] + known_webroots, - help_label="Help") + ["Enter a new webroot"] + known_webroots[::-1]) + if code == display_util.CANCEL: + raise errors.PluginError(self._INTERACTIVE_CANCEL) + elif index != 0: + return known_webroots[index - 1] + + while True: + code, webroot = display.directory_select( + "Input the webroot for {0}:".format(domain)) + if code == display_util.CANCEL: + raise errors.PluginError(self._INTERACTIVE_CANCEL) + elif code == display_util.HELP: + display.notification(display_util.DSELECT_HELP) + else: + try: + return _validate_webroot(webroot) + except errors.PluginError: + pass def _create_challenge_dirs(self): path_map = self.conf("map") From e2340a5da91036bbc68eccfec46b7d1d574e20d2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 18:40:21 -0700 Subject: [PATCH 145/317] Make all of _renew_describe_results print or not print depending on whether there have been any errors --- letsencrypt/renewal.py | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index afca174a7..672106e7b 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -4,6 +4,7 @@ import copy import glob import logging import os +import sys import traceback import six @@ -14,6 +15,7 @@ from letsencrypt import cli from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import storage +from letsencrypt.display import util as display_util from letsencrypt.plugins import disco as plugins_disco logger = logging.getLogger(__name__) @@ -200,15 +202,20 @@ def should_renew(config, lineage): return False +def report(msgs, category): + "Report results for a category of renewal outcomes" + lines = ("%s (%s)" % (m, category) for m in msgs) + return " " + "\n ".join(lines) + def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): - def notify(msg): - "A variant of print() that is silenced by -q" - zope.component.getUtility(interfaces.IDisplay).notification(msg, pause=False) + if config.quiet and (renew_failures or parse_failures): + # In case of errors, spin up a new non-quiet output display + dest = display_util.NoninteractiveDisplay(sys.stdout).log() + else: + dest = zope.component.getUtility(interfaces.IDisplay) - def report(msgs, category): - "Report results for a category of renewal outcomes" - return " " + "\n ".join("%s (%s)" % (m, category) for m in msgs) + notify = lambda msg: dest.notification(msg, pause=False) if config.dry_run: notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") @@ -224,19 +231,19 @@ def _renew_describe_results(config, renew_successes, renew_failures, "have been renewed:") notify(report(renew_successes, "success")) elif renew_failures and not renew_successes: - logger.error("All renewal attempts failed. The following certs could not be " + notify("All renewal attempts failed. The following certs could not be " "renewed:") - logger.error(report(renew_failures, "failure")) + notify(report(renew_failures, "failure")) elif renew_failures and renew_successes: - logger.error("The following certs were successfully renewed:") - logger.error(report(renew_successes, "success")) - logger.error("\nThe following certs could not be renewed:") - logger.error(report(renew_failures, "failure")) + notify("The following certs were successfully renewed:") + notify(report(renew_successes, "success")) + notify("\nThe following certs could not be renewed:") + notify(report(renew_failures, "failure")) if parse_failures: - logger.error("\nAdditionally, the following renewal configuration files " - "were invalid: ") - logger.error(parse_failures, "parsefail") + notify("\nAdditionally, the following renewal configuration files " + "were invalid: ") + notify(parse_failures, "parsefail") if config.dry_run: notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") From 8f454e0666ff763cee5ed9d336ea45555c18deb4 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 18:43:18 -0700 Subject: [PATCH 146/317] But let's not get ahead of ourselves --- letsencrypt/renewal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 672106e7b..ec8aa25ef 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -211,7 +211,7 @@ def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): if config.quiet and (renew_failures or parse_failures): # In case of errors, spin up a new non-quiet output display - dest = display_util.NoninteractiveDisplay(sys.stdout).log() + dest = display_util.NoninteractiveDisplay(sys.stdout) else: dest = zope.component.getUtility(interfaces.IDisplay) From 6c13616b1563fbbfc6467efe531d285e1cfed028 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 18:47:30 -0700 Subject: [PATCH 147/317] Less delimitation --- letsencrypt/renewal.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index ec8aa25ef..bcea6c127 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -203,19 +203,15 @@ def should_renew(config, lineage): def report(msgs, category): - "Report results for a category of renewal outcomes" + "Format a results report for a category of renewal outcomes" lines = ("%s (%s)" % (m, category) for m in msgs) return " " + "\n ".join(lines) def _renew_describe_results(config, renew_successes, renew_failures, renew_skipped, parse_failures): - if config.quiet and (renew_failures or parse_failures): - # In case of errors, spin up a new non-quiet output display - dest = display_util.NoninteractiveDisplay(sys.stdout) - else: - dest = zope.component.getUtility(interfaces.IDisplay) - notify = lambda msg: dest.notification(msg, pause=False) + out = [] + notify = out.append if config.dry_run: notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") @@ -249,6 +245,14 @@ def _renew_describe_results(config, renew_successes, renew_failures, notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") notify("** (The test certificates above have not been saved.)") + if config.quiet and (renew_failures or parse_failures): + # In case of errors, spin up a new non-quiet output display + dest = display_util.NoninteractiveDisplay(sys.stdout) + else: + dest = zope.component.getUtility(interfaces.IDisplay) + + dest.notification("\n".join(out), pause=False) + def renew_all_lineages(config): """Examine each lineage; renew if due and report results""" From 2edc288c80a75a13d187cf58d1297965f30cc548 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 1 Apr 2016 18:59:48 -0700 Subject: [PATCH 148/317] fix --csr --- letsencrypt/cli.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index edb97ae1c..0e5c15624 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -324,6 +324,11 @@ class HelpfulArgumentParser(object): logger.debug("PEM CSR parse error %s", traceback.format_exc()) raise errors.Error("Failed to parse CSR file: {0}".format(parsed_args.csr[0])) + # This is not necessary for webroot to work, however, + # obtain_certificate_from_csr requires parsed_args.domains to be set + for domain in domains: + add_domains(parsed_args, domain) + if not domains: # TODO: add CN to domains instead: raise errors.Error( From 9201f2691f55d946924c2458f74f0d85013f92c8 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 20:14:06 -0700 Subject: [PATCH 149/317] Wrangle the Reporter to also be --quiet --- letsencrypt/account.py | 2 +- letsencrypt/hooks.py | 2 +- letsencrypt/main.py | 2 +- letsencrypt/renewal.py | 8 +++----- letsencrypt/reporter.py | 15 +++++++++++---- letsencrypt/tests/cli_test.py | 2 +- letsencrypt/tests/hook_test.py | 24 ++++++++++-------------- letsencrypt/tests/reporter_test.py | 3 ++- 8 files changed, 30 insertions(+), 28 deletions(-) diff --git a/letsencrypt/account.py b/letsencrypt/account.py index c41b10c4a..464d07b18 100644 --- a/letsencrypt/account.py +++ b/letsencrypt/account.py @@ -98,7 +98,7 @@ def report_new_account(acc, config): recovery_msg = ("If you lose your account credentials, you can " "recover through e-mails sent to {0}.".format( ", ".join(acc.regr.body.emails))) - reporter.add_message(recovery_msg, reporter.HIGH_PRIORITY) + reporter.add_message(recovery_msg, reporter.MEDIUM_PRIORITY) class AccountMemoryStorage(interfaces.AccountStorage): diff --git a/letsencrypt/hooks.py b/letsencrypt/hooks.py index 6a0997708..dce17713d 100644 --- a/letsencrypt/hooks.py +++ b/letsencrypt/hooks.py @@ -62,7 +62,7 @@ def renew_hook(config, domains, lineage_path): os.environ["RENEWED_LINEAGE"] = lineage_path _run_hook(config.renew_hook) else: - print("Dry run: skipping renewal hook command: {0}".format(config.renew_hook)) + logger.warning("Dry run: skipping renewal hook command: %s", config.renew_hook) def _run_hook(shell_cmd): """Run a hook command. diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 8e2d77b3a..059b2a36d 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -687,7 +687,7 @@ def main(cli_args=sys.argv[1:]): zope.component.provideUtility(displayer) # Reporter - report = reporter.Reporter() + report = reporter.Reporter(config) zope.component.provideUtility(report) atexit.register(report.atexit_print_messages) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 00cb46de7..b3999b7ba 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -288,7 +288,7 @@ def _renew_describe_results(config, renew_successes, renew_failures, if parse_failures: notify("\nAdditionally, the following renewal configuration files " - "were invalid: ") + "were invalid: ") notify(parse_failures, "parsefail") if config.dry_run: @@ -307,9 +307,6 @@ def _renew_describe_results(config, renew_successes, renew_failures, def renew_all_lineages(config): """Examine each lineage; renew if due and report results""" - def _notify(msg): - zope.component.getUtility(interfaces.IDisplay).notification(msg, pause=False) - if config.domains != []: raise errors.Error("Currently, the renew verb is only capable of " "renewing all installed certificates that are due " @@ -324,7 +321,8 @@ def renew_all_lineages(config): renew_skipped = [] parse_failures = [] for renewal_file in renewal_conf_files(renewer_config): - _notify("Processing " + renewal_file) + disp = zope.component.getUtility(interfaces.IDisplay) + disp.notification("Processing " + renewal_file, pause=False) lineage_config = copy.deepcopy(config) # Note that this modifies config (to add back the configuration diff --git a/letsencrypt/reporter.py b/letsencrypt/reporter.py index 147928e3c..178747121 100644 --- a/letsencrypt/reporter.py +++ b/letsencrypt/reporter.py @@ -35,8 +35,9 @@ class Reporter(object): _msg_type = collections.namedtuple('ReporterMsg', 'priority text on_crash') - def __init__(self): + def __init__(self, config): self.messages = queue.PriorityQueue() + self.config = config def add_message(self, msg, priority, on_crash=True): """Adds msg to the list of messages to be printed. @@ -76,9 +77,10 @@ class Reporter(object): if not self.messages.empty(): no_exception = sys.exc_info()[0] is None bold_on = sys.stdout.isatty() - if bold_on: - print(le_util.ANSI_SGR_BOLD) - print('IMPORTANT NOTES:') + if not self.config.quiet: + if bold_on: + print(le_util.ANSI_SGR_BOLD) + print('IMPORTANT NOTES:') first_wrapper = textwrap.TextWrapper( initial_indent=' - ', subsequent_indent=(' ' * 3)) next_wrapper = textwrap.TextWrapper( @@ -86,6 +88,11 @@ class Reporter(object): subsequent_indent=first_wrapper.subsequent_indent) while not self.messages.empty(): msg = self.messages.get() + if self.config.quiet: + # In --quiet mode, we only print high priority messages that + # are flagged for crash cases + if not (msg.priority == self.HIGH_PRIORITY and msg.on_crash): + continue if no_exception or msg.on_crash: if bold_on and msg.priority > self.HIGH_PRIORITY: sys.stdout.write(le_util.ANSI_SGR_RESET) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index de8b0c7e8..7819eff66 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -373,7 +373,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods try: self._call(['--csr', CSR]) except errors.Error as e: - assert "Please try the certonly" in e.message + assert "Please try the certonly" in repr(e) return assert False, "Expected supplying --csr to fail with default verb" diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py index 6506eea2c..62d3d5986 100644 --- a/letsencrypt/tests/hook_test.py +++ b/letsencrypt/tests/hook_test.py @@ -3,7 +3,6 @@ import os import unittest -import sys import mock @@ -47,12 +46,14 @@ class HookTest(unittest.TestCase): mockwhich.return_value = None self.assertEqual(hooks._prog("funky"), None) - def _test_a_hook(self, config, hook_function, calls_expected): - with mock.patch('letsencrypt.hooks.logger'): - with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook: - hook_function(config) - hook_function(config) - self.assertEqual(mock_run_hook.call_count, calls_expected) + @mock.patch('letsencrypt.hooks.logger') + def _test_a_hook(self, config, hook_function, calls_expected, mock_logger): + mock_logger.warning = mock.MagicMock() + with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook: + hook_function(config) + hook_function(config) + self.assertEqual(mock_run_hook.call_count, calls_expected) + return mock_logger.warning def test_pre_hook(self): config = mock.MagicMock(pre_hook="true") @@ -78,13 +79,8 @@ class HookTest(unittest.TestCase): self.assertEqual(os.environ["RENEWED_LINEAGE"], "thing") config = mock.MagicMock(renew_hook="true", dry_run=True) - if sys.version_info < (2, 7): - # the print() function is not mockable in py26 - self._test_a_hook(config, rhook, 0) - else: - with mock.patch("letsencrypt.hooks.print") as mock_print: - self._test_a_hook(config, rhook, 0) - self.assertEqual(mock_print.call_count, 2) + mock_warn = self._test_a_hook(config, rhook, 0) + self.assertEqual(mock_warn.call_count, 2) @mock.patch('letsencrypt.hooks.Popen') def test_run_hook(self, mock_popen): diff --git a/letsencrypt/tests/reporter_test.py b/letsencrypt/tests/reporter_test.py index 26a1105c8..191c1b933 100644 --- a/letsencrypt/tests/reporter_test.py +++ b/letsencrypt/tests/reporter_test.py @@ -1,4 +1,5 @@ """Tests for letsencrypt.reporter.""" +import mock import sys import unittest @@ -10,7 +11,7 @@ class ReporterTest(unittest.TestCase): def setUp(self): from letsencrypt import reporter - self.reporter = reporter.Reporter() + self.reporter = reporter.Reporter(mock.MagicMock(quiet=False)) self.old_stdout = sys.stdout sys.stdout = six.StringIO() From 7fbd800ddd96e37d6293afc6b9580c8d4cb040b6 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 1 Apr 2016 20:17:06 -0700 Subject: [PATCH 150/317] lint --- letsencrypt/tests/hook_test.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/letsencrypt/tests/hook_test.py b/letsencrypt/tests/hook_test.py index 62d3d5986..3751133cf 100644 --- a/letsencrypt/tests/hook_test.py +++ b/letsencrypt/tests/hook_test.py @@ -46,14 +46,14 @@ class HookTest(unittest.TestCase): mockwhich.return_value = None self.assertEqual(hooks._prog("funky"), None) - @mock.patch('letsencrypt.hooks.logger') - def _test_a_hook(self, config, hook_function, calls_expected, mock_logger): - mock_logger.warning = mock.MagicMock() - with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook: - hook_function(config) - hook_function(config) - self.assertEqual(mock_run_hook.call_count, calls_expected) - return mock_logger.warning + def _test_a_hook(self, config, hook_function, calls_expected): + with mock.patch('letsencrypt.hooks.logger') as mock_logger: + mock_logger.warning = mock.MagicMock() + with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook: + hook_function(config) + hook_function(config) + self.assertEqual(mock_run_hook.call_count, calls_expected) + return mock_logger.warning def test_pre_hook(self): config = mock.MagicMock(pre_hook="true") From d401595396c660e829325b111323daa2623fde5f Mon Sep 17 00:00:00 2001 From: Felix Schwarz Date: Sat, 2 Apr 2016 10:50:18 +0200 Subject: [PATCH 151/317] fix spelling mistake in CLI help text --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 199b98311..197169d95 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -721,7 +721,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): help="Command to be run in a shell once for each successfully renewed certificate." "For this command, the shell variable $RENEWED_LINEAGE will point to the" "config live subdirectory containing the new certs and keys; the shell variable " - "$RENEWED_DOMAINS will conatain a space-delimited list of renewed cert domains") + "$RENEWED_DOMAINS will contain a space-delimited list of renewed cert domains") helpful.add_deprecated_argument("--agree-dev-preview", 0) From 2f3035de0e10e22e74503ed094c63b3d03201e3d Mon Sep 17 00:00:00 2001 From: Eric Engestrom Date: Sat, 2 Apr 2016 15:33:13 +0100 Subject: [PATCH 152/317] Fix spelling mistakes --- .../tests/apache-conf-files/passing/example-ssl.conf | 2 +- letsencrypt/renewal.py | 2 +- tests/letstest/scripts/test_renew_standalone.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf index 466ac9ce3..31deb7647 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf @@ -39,7 +39,7 @@ # certificate chain for the server certificate. Alternatively # the referenced file can be the same as SSLCertificateFile # when the CA certificates are directly appended to the server - # certificate for convinience. + # certificate for convenience. #SSLCertificateChainFile /etc/apache2/ssl.crt/server-ca.crt # Certificate Authority (CA): diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 8a172098b..8fcb013d3 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -78,7 +78,7 @@ def _reconstitute(config, full_path): _restore_plugin_configs(config, renewalparams) except (ValueError, errors.Error) as error: logger.warning( - "An error occured while parsing %s. The error was %s. " + "An error occurred while parsing %s. The error was %s. " "Skipping the file.", full_path, error.message) logger.debug("Traceback was:\n%s", traceback.format_exc()) return None diff --git a/tests/letstest/scripts/test_renew_standalone.sh b/tests/letstest/scripts/test_renew_standalone.sh index d90ae9ab6..9bce17a60 100755 --- a/tests/letstest/scripts/test_renew_standalone.sh +++ b/tests/letstest/scripts/test_renew_standalone.sh @@ -14,7 +14,7 @@ elif [ -f /etc/redhat-release ] ; then echo "Bootstrapping dependencies for RedHat-based OSes..." $SUDO bootstrap/_rpm_common.sh else - echo "Dont have bootstrapping for this OS!" + echo "Don't have bootstrapping for this OS!" exit 1 fi From f5e0aca89129918bc488a97452b7714274ebe956 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 2 Apr 2016 12:25:10 -0700 Subject: [PATCH 153/317] Try things with just print() --- letsencrypt/main.py | 2 +- letsencrypt/renewal.py | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index 059b2a36d..ac0ec69e8 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -674,7 +674,7 @@ def main(cli_args=sys.argv[1:]): # Displayer if config.quiet: config.noninteractive_mode = True - displayer = display_util.NoninteractiveDisplay(open("/dev/null", "w")) + displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w")) elif config.noninteractive_mode: displayer = display_util.NoninteractiveDisplay(sys.stdout) elif config.text_mode: diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index b3999b7ba..55a7d8262 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -295,13 +295,9 @@ def _renew_describe_results(config, renew_successes, renew_failures, notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") notify("** (The test certificates above have not been saved.)") - if config.quiet and (renew_failures or parse_failures): - # In case of errors, spin up a new non-quiet output display - dest = display_util.NoninteractiveDisplay(sys.stdout) - else: - dest = zope.component.getUtility(interfaces.IDisplay) - - dest.notification("\n".join(out), pause=False) + if config.quiet and not (renew_failures or parse_failures): + return + print("\n".join(out)) def renew_all_lineages(config): From 2d502ba98e83e18850c10f56d3c253a328e9bbbb Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 2 Apr 2016 12:41:37 -0700 Subject: [PATCH 154/317] Handle renewal conf files without a "server" entry Fixes: #2750 --- letsencrypt/renewal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 8fcb013d3..45c1cdb46 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -230,7 +230,8 @@ def _avoid_invalidating_lineage(config, lineage, original_server): def renew_cert(config, domains, le_client, lineage): "Renew a certificate lineage." - original_server = lineage.configuration["renewalparams"]["server"] + renewal_params = lineage.configuration["renewalparams"] + original_server = renewal_params.get("server", cli.flag_default("server")) _avoid_invalidating_lineage(config, lineage, original_server) new_certr, new_chain, new_key, _ = le_client.obtain_certificate(domains) if config.dry_run: From 6a11e171e8539aaec8abfb5926c8156c2cc35348 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Sat, 2 Apr 2016 12:48:55 -0700 Subject: [PATCH 155/317] lintmonster --- letsencrypt/renewal.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/letsencrypt/renewal.py b/letsencrypt/renewal.py index 55a7d8262..c65c59822 100644 --- a/letsencrypt/renewal.py +++ b/letsencrypt/renewal.py @@ -4,7 +4,6 @@ import copy import glob import logging import os -import sys import traceback import six @@ -21,7 +20,6 @@ from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import hooks from letsencrypt import storage -from letsencrypt.display import util as display_util from letsencrypt.plugins import disco as plugins_disco logger = logging.getLogger(__name__) From 119c3f125a0431ad70260f887c930ec30ab9d85a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 09:07:26 -0700 Subject: [PATCH 156/317] Revert csr_domain logic --- letsencrypt/cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 377ee1135..ab328747f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -380,11 +380,11 @@ class HelpfulArgumentParser(object): % parsed_args.csr[0]) parsed_args.actual_csr = (csr, typ) - # If CSR domains are not a superset of the domains provided by the CLI - if set(parsed_args.domains) - set(domains): + csr_domains, config_domains = set(domains), set(parsed_args.domains) + if csr_domains != config_domains: raise errors.ConfigurationError( "Inconsistent domain requests:\nFrom the CSR: {0}\nFrom command line/config: {1}" - .format(", ".join(domains), ", ".join(parsed_args.domains))) + .format(", ".join(csr_domains), ", ".join(config_domains))) def determine_verb(self): From 22c924bb1c261f0308815f6d3d22cf60385558ff Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 09:45:48 -0700 Subject: [PATCH 157/317] more interactive webroot polish --- letsencrypt/plugins/webroot.py | 46 ++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index ac4c3c1ec..2052d4edb 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -38,6 +38,16 @@ to serve all files under specified web root ({0}).""" _INTERACTIVE_CANCEL = ("Every requested domain must have a " "webroot when using the webroot plugin.") + _INPUT_HELP_FMT = ( + "To use the webroot plugin, you need to have an HTTP server " + "running on this system serving files for the requested " + "domain. Additionally, this server should be serving all " + "files contained in a public_html or webroot directory. The " + "webroot plugin works by temporarily saving necessary " + "resources in the HTTP server's webroot directory to pass " + "domain validation challenges.\n\nTo continue, you need to " + "provide the webroot directory for {0}.") + def more_info(self): # pylint: disable=missing-docstring,no-self-use return self.MORE_INFO.format(self.conf("path")) @@ -72,13 +82,13 @@ to serve all files under specified web root ({0}).""" pass def perform(self, achalls): # pylint: disable=missing-docstring - self._get_webroots(achalls) + self._set_webroots(achalls) self._create_challenge_dirs() return [self._perform_single(achall) for achall in achalls] - def _get_webroots(self, achalls): + def _set_webroots(self, achalls): if self.conf("path"): webroot_path = self.conf("path")[-1] for achall in achalls: @@ -89,22 +99,32 @@ to serve all files under specified web root ({0}).""" if achall.domain not in self.conf("map"): new_webroot = self._prompt_for_webroot(achall.domain, known_webroots) + # Put the most recently input + # webroot first for easy selection try: known_webroots.remove(new_webroot) except ValueError: pass - known_webroots.append(new_webroot) + known_webroots.insert(0, new_webroot) self.conf("map")[achall.domain] = new_webroot def _prompt_for_webroot(self, domain, known_webroots): display = zope.component.getUtility(interfaces.IDisplay) - code, index = display.menu( - "Select the webroot for {0}:".format(domain), - ["Enter a new webroot"] + known_webroots[::-1]) - if code == display_util.CANCEL: - raise errors.PluginError(self._INTERACTIVE_CANCEL) - elif index != 0: - return known_webroots[index - 1] + + while True: + code, index = display.menu( + "Select the webroot for {0}:".format(domain), + ["Enter a new webroot"] + known_webroots, + help_label="Help") + if code == display_util.CANCEL: + raise errors.PluginError(self._INTERACTIVE_CANCEL) + elif code == display_util.HELP: + display.notification(self._INPUT_HELP_FMT.format(domain)) + else: # code == display_util.OK + if index == 0: + break + else: + return known_webroots[index - 1] while True: code, webroot = display.directory_select( @@ -112,7 +132,11 @@ to serve all files under specified web root ({0}).""" if code == display_util.CANCEL: raise errors.PluginError(self._INTERACTIVE_CANCEL) elif code == display_util.HELP: - display.notification(display_util.DSELECT_HELP) + # Help can currently only be selected + # when using the ncurses interface + display.notification(''.join( + (self._INPUT_HELP_FMT.format(domain), + "\n\n", display_util.DSELECT_HELP,))) else: try: return _validate_webroot(webroot) From bf0c2306c625ea56531e351c781b9f26250618f1 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 09:49:56 -0700 Subject: [PATCH 158/317] Try and make ncurses less ugly --- letsencrypt/plugins/webroot.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 2052d4edb..b4de56f5b 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -38,15 +38,14 @@ to serve all files under specified web root ({0}).""" _INTERACTIVE_CANCEL = ("Every requested domain must have a " "webroot when using the webroot plugin.") - _INPUT_HELP_FMT = ( + _INPUT_HELP = ( "To use the webroot plugin, you need to have an HTTP server " "running on this system serving files for the requested " "domain. Additionally, this server should be serving all " "files contained in a public_html or webroot directory. The " "webroot plugin works by temporarily saving necessary " "resources in the HTTP server's webroot directory to pass " - "domain validation challenges.\n\nTo continue, you need to " - "provide the webroot directory for {0}.") + "domain validation challenges.") def more_info(self): # pylint: disable=missing-docstring,no-self-use return self.MORE_INFO.format(self.conf("path")) @@ -119,7 +118,7 @@ to serve all files under specified web root ({0}).""" if code == display_util.CANCEL: raise errors.PluginError(self._INTERACTIVE_CANCEL) elif code == display_util.HELP: - display.notification(self._INPUT_HELP_FMT.format(domain)) + display.notification(self._INPUT_HELP) else: # code == display_util.OK if index == 0: break @@ -134,9 +133,7 @@ to serve all files under specified web root ({0}).""" elif code == display_util.HELP: # Help can currently only be selected # when using the ncurses interface - display.notification(''.join( - (self._INPUT_HELP_FMT.format(domain), - "\n\n", display_util.DSELECT_HELP,))) + display.notification(display_util.DSELECT_HELP) else: try: return _validate_webroot(webroot) From 987aa8237188c201fbd9b7aae49094a788b92201 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 10:32:01 -0700 Subject: [PATCH 159/317] more UI polish --- letsencrypt/plugins/webroot.py | 57 ++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index b4de56f5b..9bd9dc90d 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -35,18 +35,6 @@ 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}).""" - _INTERACTIVE_CANCEL = ("Every requested domain must have a " - "webroot when using the webroot plugin.") - - _INPUT_HELP = ( - "To use the webroot plugin, you need to have an HTTP server " - "running on this system serving files for the requested " - "domain. Additionally, this server should be serving all " - "files contained in a public_html or webroot directory. The " - "webroot plugin works by temporarily saving necessary " - "resources in the HTTP server's webroot directory to pass " - "domain validation challenges.") - def more_info(self): # pylint: disable=missing-docstring,no-self-use return self.MORE_INFO.format(self.conf("path")) @@ -108,6 +96,17 @@ to serve all files under specified web root ({0}).""" self.conf("map")[achall.domain] = new_webroot def _prompt_for_webroot(self, domain, known_webroots): + webroot = None + + while webroot is None: + webroot = self._prompt_with_webroot_list(domain, known_webroots) + + if webroot is None: + webroot = self._prompt_for_new_webroot(domain) + + return webroot + + def _prompt_with_webroot_list(self, domain, known_webroots): display = zope.component.getUtility(interfaces.IDisplay) while True: @@ -116,29 +115,39 @@ to serve all files under specified web root ({0}).""" ["Enter a new webroot"] + known_webroots, help_label="Help") if code == display_util.CANCEL: - raise errors.PluginError(self._INTERACTIVE_CANCEL) + raise errors.PluginError( + "Every requested domain must have a " + "webroot when using the webroot plugin.") elif code == display_util.HELP: - display.notification(self._INPUT_HELP) + display.notification( + "To use the webroot plugin, you need to have an " + "HTTP server running on this system serving files " + "for the requested domain. Additionally, this " + "server should be serving all files contained in a " + "public_html or webroot directory. The webroot " + "plugin works by temporarily saving necessary " + "resources in the HTTP server's webroot directory " + "to pass domain validation challenges.") else: # code == display_util.OK - if index == 0: - break - else: - return known_webroots[index - 1] + return None if index == 0 else known_webroots[index - 1] + + def _prompt_for_new_webroot(self, domain): + display = zope.component.getUtility(interfaces.IDisplay) while True: code, webroot = display.directory_select( "Input the webroot for {0}:".format(domain)) - if code == display_util.CANCEL: - raise errors.PluginError(self._INTERACTIVE_CANCEL) - elif code == display_util.HELP: + if code == display_util.HELP: # Help can currently only be selected # when using the ncurses interface display.notification(display_util.DSELECT_HELP) - else: + elif code == display_util.CANCEL: + return None + else: # code == display_util.OK try: return _validate_webroot(webroot) - except errors.PluginError: - pass + except errors.PluginError as error: + display.notification(str(error)) def _create_challenge_dirs(self): path_map = self.conf("map") From 17c495732d5bcc313f805562ee616b5cd4811172 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 10:38:34 -0700 Subject: [PATCH 160/317] Don't pause when showing errors --- letsencrypt/plugins/webroot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 9bd9dc90d..a425f0d20 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -147,7 +147,7 @@ to serve all files under specified web root ({0}).""" try: return _validate_webroot(webroot) except errors.PluginError as error: - display.notification(str(error)) + display.notification(str(error), pause=False) def _create_challenge_dirs(self): path_map = self.conf("map") From 59d7c44bd6f419afaeb83a16341b027913e68fbf Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 4 Apr 2016 10:38:49 -0700 Subject: [PATCH 161/317] Avoid all terminal codes in --quiet mode --- letsencrypt/reporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/reporter.py b/letsencrypt/reporter.py index 178747121..03f5ca200 100644 --- a/letsencrypt/reporter.py +++ b/letsencrypt/reporter.py @@ -102,5 +102,5 @@ class Reporter(object): if len(lines) > 1: print("\n".join( next_wrapper.fill(line) for line in lines[1:])) - if bold_on: + if bold_on and not self.config.quiet: sys.stdout.write(le_util.ANSI_SGR_RESET) From 12006fb5f51d37124fea101670f95dfe2969296e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 4 Apr 2016 10:53:11 -0700 Subject: [PATCH 162/317] cli_test: _call() now allows stdout content inspection --- letsencrypt/tests/cli_test.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 7819eff66..413e0965c 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -65,10 +65,12 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _call_no_clientmock(self, args): "Run the client with output streams mocked out" args = self.standard_args + args - with mock.patch('letsencrypt.main.sys.stdout') as stdout: + + toy_stdout = six.StringIO() + with mock.patch('letsencrypt.main.sys.stdout', new=toy_stdout): with mock.patch('letsencrypt.main.sys.stderr') as stderr: ret = main.main(args[:]) # NOTE: parser can alter its args! - return ret, stdout, stderr + return ret, toy_stdout, stderr def _call_stdout(self, args): """ @@ -283,7 +285,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods plugins.visible.assert_called_once_with() plugins.visible().ifaces.assert_called_once_with(ifaces) filtered = plugins.visible().ifaces() - stdout.write.called_once_with(str(filtered)) + #stdout.write.called_once_with(str(filtered)) + self.assertEqual(stdout.getvalue().strip(), str(filtered)) @mock.patch('letsencrypt.main.plugins_disco') @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') @@ -298,7 +301,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(filtered.init.call_count, 1) filtered.verify.assert_called_once_with(ifaces) verified = filtered.verify() - stdout.write.called_once_with(str(verified)) + self.assertEqual(stdout.getvalue().strip(), str(verified)) + #stdout.write.called_once_with(str(verified)) @mock.patch('letsencrypt.main.plugins_disco') @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') @@ -315,7 +319,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods verified.prepare.assert_called_once_with() verified.available.assert_called_once_with() available = verified.available() - stdout.write.called_once_with(str(available)) + self.assertEqual(stdout.getvalue().strip(), str(available)) + #stdout.write.called_once_with(str(available)) def test_certonly_abspath(self): cert = 'cert' From 7458324932e0776631f0870fba50ef13a5c01199 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 10:57:02 -0700 Subject: [PATCH 163/317] logging++ --- letsencrypt/plugins/webroot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index a425f0d20..677130f12 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -78,6 +78,8 @@ to serve all files under specified web root ({0}).""" def _set_webroots(self, achalls): if self.conf("path"): webroot_path = self.conf("path")[-1] + logger.info("Using the webroot path %s for all unmatched domains.", + webroot_path) for achall in achalls: self.conf("map").setdefault(achall.domain, webroot_path) else: From 13e0fbb1f1b2281e0fa91dfc06788b372977765f Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 4 Apr 2016 11:06:37 -0700 Subject: [PATCH 164/317] _call can now handle the _call_stdout case --- letsencrypt/tests/cli_test.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 413e0965c..5cb6c909c 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -56,33 +56,22 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods # pylint: disable=protected-access cli._parser = cli.set_by_cli.detector = None - def _call(self, args): + def _call(self, args, stdout=None): "Run the cli with output streams and actual client mocked out" with mock.patch('letsencrypt.main.client') as client: - ret, stdout, stderr = self._call_no_clientmock(args) + ret, stdout, stderr = self._call_no_clientmock(args, stdout) return ret, stdout, stderr, client - def _call_no_clientmock(self, args): + def _call_no_clientmock(self, args, stdout=None): "Run the client with output streams mocked out" args = self.standard_args + args - toy_stdout = six.StringIO() + toy_stdout = stdout if stdout else six.StringIO() with mock.patch('letsencrypt.main.sys.stdout', new=toy_stdout): with mock.patch('letsencrypt.main.sys.stderr') as stderr: ret = main.main(args[:]) # NOTE: parser can alter its args! return ret, toy_stdout, stderr - def _call_stdout(self, args): - """ - Variant of _call that preserves stdout so that it can be mocked by the - caller. - """ - args = self.standard_args + args - with mock.patch('letsencrypt.main.sys.stderr') as stderr: - with mock.patch('letsencrypt.main.client') as client: - ret = main.main(args[:]) # NOTE: parser can alter its args! - return ret, None, stderr, client - def test_no_flags(self): with mock.patch('letsencrypt.main.run') as mock_run: self._call([]) @@ -92,10 +81,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "Run a command, and return the ouput string for scrutiny" output = six.StringIO() - with mock.patch('letsencrypt.main.sys.stdout', new=output): - self.assertRaises(SystemExit, self._call_stdout, args) - out = output.getvalue() - return out + self.assertRaises(SystemExit, self._call, args, output) + out = output.getvalue() + return out def test_help(self): self.assertRaises(SystemExit, self._call, ['--help']) @@ -285,7 +273,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods plugins.visible.assert_called_once_with() plugins.visible().ifaces.assert_called_once_with(ifaces) filtered = plugins.visible().ifaces() - #stdout.write.called_once_with(str(filtered)) self.assertEqual(stdout.getvalue().strip(), str(filtered)) @mock.patch('letsencrypt.main.plugins_disco') @@ -302,7 +289,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods filtered.verify.assert_called_once_with(ifaces) verified = filtered.verify() self.assertEqual(stdout.getvalue().strip(), str(verified)) - #stdout.write.called_once_with(str(verified)) @mock.patch('letsencrypt.main.plugins_disco') @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') @@ -320,7 +306,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods verified.available.assert_called_once_with() available = verified.available() self.assertEqual(stdout.getvalue().strip(), str(available)) - #stdout.write.called_once_with(str(available)) def test_certonly_abspath(self): cert = 'cert' From f8867ab357666a8b6ba486198d3ead862fabab69 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 4 Apr 2016 11:14:04 -0700 Subject: [PATCH 165/317] Test for --quiet renew --- letsencrypt/tests/cli_test.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 5cb6c909c..7f88db3bd 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -560,6 +560,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_certr = mock.MagicMock() mock_key = mock.MagicMock(pem='pem_key') mock_client = mock.MagicMock() + stdout = None mock_client.obtain_certificate.return_value = (mock_certr, 'chain', mock_key, 'csr') try: @@ -579,7 +580,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if extra_args: args += extra_args try: - ret, _, _, _ = self._call(args) + ret, stdout, _, _ = self._call(args) if ret: print("Returned", ret) raise AssertionError(ret) @@ -602,10 +603,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf: self.assertTrue(log_out in lf.read()) - return mock_lineage, mock_get_utility + return mock_lineage, mock_get_utility, stdout def test_certonly_renewal(self): - lineage, get_utility = self._test_renewal_common(True, []) + lineage, get_utility, _ = self._test_renewal_common(True, []) self.assertEqual(lineage.save_successor.call_count, 1) lineage.update_all_links_to.assert_called_once_with( lineage.latest_common_version()) @@ -615,17 +616,18 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_certonly_renewal_triggers(self): # --dry-run should force renewal - _, get_utility = self._test_renewal_common(False, ['--dry-run', '--keep'], - log_out="simulating renewal") + _, get_utility, _ = self._test_renewal_common(False, ['--dry-run', '--keep'], + log_out="simulating renewal") self.assertEqual(get_utility().add_message.call_count, 1) self.assertTrue('dry run' in get_utility().add_message.call_args[0][0]) - _, _ = self._test_renewal_common(False, ['--renew-by-default', '-tvv', '--debug'], - log_out="Auto-renewal forced") + self._test_renewal_common(False, ['--renew-by-default', '-tvv', '--debug'], + log_out="Auto-renewal forced") self.assertEqual(get_utility().add_message.call_count, 1) - _, _ = self._test_renewal_common(False, ['-tvv', '--debug', '--keep'], - log_out="not yet due", should_renew=False) + self._test_renewal_common(False, ['-tvv', '--debug', '--keep'], + log_out="not yet due", should_renew=False) + def _dump_log(self): with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf: @@ -650,6 +652,19 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args = ["renew", "--dry-run", "-tvv"] self._test_renewal_common(True, [], args=args, should_renew=True) + def test_quiet_renew(self): + self._make_test_renewal_conf('sample-renewal.conf') + args = ["renew", "--dry-run"] + _, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True) + out = stdout.getvalue() + self.assertTrue("renew" in out) + + args = ["renew", "--dry-run", "-q"] + _, _, stdout = self._test_renewal_common(True, [], args=args, should_renew=True) + out = stdout.getvalue() + self.assertEqual("", out) + + @mock.patch("letsencrypt.cli.set_by_cli") def test_ancient_webroot_renewal_conf(self, mock_set_by_cli): mock_set_by_cli.return_value = False From b567a542064d5036f437f1a65b6402a334bc306a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 4 Apr 2016 11:16:37 -0700 Subject: [PATCH 166/317] Really never echo terminal codes in quiet mode --- letsencrypt/reporter.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt/reporter.py b/letsencrypt/reporter.py index 03f5ca200..f3ab93763 100644 --- a/letsencrypt/reporter.py +++ b/letsencrypt/reporter.py @@ -95,8 +95,9 @@ class Reporter(object): continue if no_exception or msg.on_crash: if bold_on and msg.priority > self.HIGH_PRIORITY: - sys.stdout.write(le_util.ANSI_SGR_RESET) - bold_on = False + if not self.config.quiet: + sys.stdout.write(le_util.ANSI_SGR_RESET) + bold_on = False lines = msg.text.splitlines() print(first_wrapper.fill(lines[0])) if len(lines) > 1: From 625e9660fede8dad9f635351ed04a6a1fe2d38a5 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 11:23:53 -0700 Subject: [PATCH 167/317] test cancel/help of _prompt_with_webroot_list --- letsencrypt/plugins/webroot_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 0c7a2a3b0..bfcfe4f9c 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -10,12 +10,14 @@ import tempfile import unittest import mock +import six from acme import challenges from acme import jose from letsencrypt import achallenges from letsencrypt import errors +from letsencrypt.display import util as display_util from letsencrypt.tests import acme_util from letsencrypt.tests import test_util @@ -58,6 +60,22 @@ class AuthenticatorTest(unittest.TestCase): def test_prepare(self): self.auth.prepare() # shouldn't raise any exceptions + @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") + def test_webroot_from_list_help_and_cancel(self, mock_get_utility): + self.config.webroot_path = [] + self.config.webroot_map = {"otherthing.com": self.path} + + mock_display = mock_get_utility() + mock_display.menu.side_effect = [(display_util.HELP, -1), + (display_util.CANCEL, -1)] + self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) + self.assertTrue(mock_display.notification.called) + for call in mock_display.menu.call_args_list: + self.assertTrue(self.achall.domain in call[0][0]) + self.assertTrue(all( + webroot in call[0][1] + for webroot in six.itervalues(self.config.webroot_map))) + def test_perform_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} From 713fb3433edc2f6d4b3f2e1c31003600edfc1769 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 11:29:51 -0700 Subject: [PATCH 168/317] give _prompt_with_webroot_list full test coverage --- letsencrypt/plugins/webroot_test.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index bfcfe4f9c..64c098995 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -60,6 +60,23 @@ class AuthenticatorTest(unittest.TestCase): def test_prepare(self): self.auth.prepare() # shouldn't raise any exceptions + @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") + def test_webroot_from_list(self, mock_get_utility): + self.config.webroot_path = [] + self.config.webroot_map = {"otherthing.com": self.path} + mock_display = mock_get_utility() + mock_display.menu.return_value = (display_util.OK, 1,) + + self.auth.perform([self.achall]) + self.assertTrue(mock_display.menu.called) + for call in mock_display.menu.call_args_list: + self.assertTrue(self.achall.domain in call[0][0]) + self.assertTrue(all( + webroot in call[0][1] + for webroot in six.itervalues(self.config.webroot_map))) + self.assertEqual(self.config.webroot_map[self.achall.domain], + self.path) + @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") def test_webroot_from_list_help_and_cancel(self, mock_get_utility): self.config.webroot_path = [] @@ -70,6 +87,7 @@ class AuthenticatorTest(unittest.TestCase): (display_util.CANCEL, -1)] self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) self.assertTrue(mock_display.notification.called) + self.assertTrue(mock_display.menu.called) for call in mock_display.menu.call_args_list: self.assertTrue(self.achall.domain in call[0][0]) self.assertTrue(all( From e01cb704a33b2e8c118994f63db80fdcf7b677f2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 12:38:03 -0700 Subject: [PATCH 169/317] test _prompt_for_new_webroot --- letsencrypt/plugins/webroot_test.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 64c098995..2c9de900f 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -83,8 +83,8 @@ class AuthenticatorTest(unittest.TestCase): self.config.webroot_map = {"otherthing.com": self.path} mock_display = mock_get_utility() - mock_display.menu.side_effect = [(display_util.HELP, -1), - (display_util.CANCEL, -1)] + mock_display.menu.side_effect = ((display_util.HELP, -1), + (display_util.CANCEL, -1),) self.assertRaises(errors.PluginError, self.auth.perform, [self.achall]) self.assertTrue(mock_display.notification.called) self.assertTrue(mock_display.menu.called) @@ -94,6 +94,29 @@ class AuthenticatorTest(unittest.TestCase): webroot in call[0][1] for webroot in six.itervalues(self.config.webroot_map))) + @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") + def test_new_webroot(self, mock_get_utility): + self.config.webroot_path = [] + self.config.webroot_map = {} + + imaginary_dir = os.path.join(os.sep, "imaginary", "dir") + + mock_display = mock_get_utility() + mock_display.menu.return_value = (display_util.OK, 0,) + mock_display.directory_select.side_effect = ( + (display_util.HELP, -1,), (display_util.CANCEL, -1,), + (display_util.OK, imaginary_dir,), (display_util.OK, self.path,),) + self.auth.perform([self.achall]) + + self.assertTrue(mock_display.notification.called) + for call in mock_display.notification.call_args_list: + self.assertTrue(imaginary_dir in call[0][0] or + display_util.DSELECT_HELP == call[0][0]) + + self.assertTrue(mock_display.directory_select.called) + for call in mock_display.directory_select.call_args_list: + self.assertTrue(self.achall.domain in call[0][0]) + def test_perform_missing_root(self): self.config.webroot_path = None self.config.webroot_map = {} From c7ef17df920eebc8c3ca757077bb988cdbc96040 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:05:46 -0700 Subject: [PATCH 170/317] fix references to old prepare/perform --- letsencrypt/plugins/webroot_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 2c9de900f..fe57a2b5c 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -122,7 +122,7 @@ class AuthenticatorTest(unittest.TestCase): self.config.webroot_map = {} self.assertRaises(errors.PluginError, self.auth.perform, []) - def test_prepare_reraises_other_errors(self): + def test_perform_reraises_other_errors(self): self.auth.full_path = os.path.join(self.path, "null") permission_canary = os.path.join(self.path, "rnd") with open(permission_canary, "w") as f: @@ -139,7 +139,7 @@ class AuthenticatorTest(unittest.TestCase): @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_eacces(self, mock_chown): mock_chown.side_effect = OSError(errno.EACCES, "msg") - self.auth.prepare() # exception caught and logged + self.auth.perform([self.achall]) # exception caught and logged @mock.patch("letsencrypt.plugins.webroot.os.chown") def test_failed_chown_not_eacces(self, mock_chown): From 641f0c7422e0d12807d5db8e616e5870609019d7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:06:00 -0700 Subject: [PATCH 171/317] simplify error handling --- letsencrypt/plugins/webroot.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 677130f12..24d8d3c1c 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -196,24 +196,13 @@ to serve all files under specified web root ({0}).""" finally: os.umask(old_umask) - def _get_root_path(self, achall): - try: - path = self.full_roots[achall.domain] - except KeyError: - raise errors.PluginError("Missing --webroot-path for domain: {0}" - .format(achall.domain)) - if not os.path.exists(path): - raise errors.PluginError("Mysteriously missing path {0} for domain: {1}" - .format(path, achall.domain)) - return path - def _get_validation_path(self, root_path, achall): return os.path.join(root_path, achall.chall.encode("token")) def _perform_single(self, achall): response, validation = achall.response_and_validation() - root_path = self._get_root_path(achall) + root_path = self.full_roots[achall.domain] validation_path = self._get_validation_path(root_path, achall) logger.debug("Attempting to save validation to %s", validation_path) @@ -232,7 +221,7 @@ to serve all files under specified web root ({0}).""" def cleanup(self, achalls): # pylint: disable=missing-docstring for achall in achalls: - root_path = self._get_root_path(achall) + root_path = self.full_roots[achall.domain] validation_path = self._get_validation_path(root_path, achall) logger.debug("Removing %s", validation_path) os.remove(validation_path) From 73e2fafba4f364068365f8b0a83f712f6d24a08a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:26:13 -0700 Subject: [PATCH 172/317] Add webroot map tests --- letsencrypt/plugins/webroot_test.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index fe57a2b5c..7dbe16d08 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -2,6 +2,7 @@ from __future__ import print_function +import argparse import errno import os import shutil @@ -44,6 +45,10 @@ class AuthenticatorTest(unittest.TestCase): webroot_map={"thing.com": self.path}) self.auth = Authenticator(self.config, "webroot") + self.parser = argparse.ArgumentParser() + self.parser.add_argument("--domains", default=[]) + self.auth.inject_parser_options(self.parser, self.auth.name) + def tearDown(self): shutil.rmtree(self.path) @@ -224,5 +229,10 @@ class AuthenticatorTest(unittest.TestCase): self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) + def test_webroot_map_action(self): + args = self.parser.parse_args( + ["--webroot-map", '{{"thing.com":"{0}"}}'.format(self.path)]) + self.assertEqual(args.webroot_map, self.config.webroot_map) + if __name__ == "__main__": unittest.main() # pragma: no cover From ed0c3810316847bef3488f1d2101039b93d6ae0a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:35:26 -0700 Subject: [PATCH 173/317] Add domain before webroot test --- letsencrypt/plugins/webroot_test.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 7dbe16d08..b92c0759f 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -46,7 +46,8 @@ class AuthenticatorTest(unittest.TestCase): self.auth = Authenticator(self.config, "webroot") self.parser = argparse.ArgumentParser() - self.parser.add_argument("--domains", default=[]) + self.parser.add_argument("-d", "--domains", + action="append", default=[]) self.auth.inject_parser_options(self.parser, self.auth.name) def tearDown(self): @@ -234,5 +235,14 @@ class AuthenticatorTest(unittest.TestCase): ["--webroot-map", '{{"thing.com":"{0}"}}'.format(self.path)]) self.assertEqual(args.webroot_map, self.config.webroot_map) + def test_domain_before_webroot(self): + args = self.parser.parse_args( + "-d {0} -w {1}".format(self.achall.domain, self.path).split()) + self.auth.config = args + self.auth.perform([self.achall]) + self.assertEqual(self.auth.config.webroot_map, + self.config.webroot_map) + + if __name__ == "__main__": unittest.main() # pragma: no cover From c716003b1fd69ec94cdec018a63e60c613ce5f8f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:38:53 -0700 Subject: [PATCH 174/317] add test for domain before multiple webroots --- letsencrypt/plugins/webroot_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index b92c0759f..155f1cf51 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -243,6 +243,12 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(self.auth.config.webroot_map, self.config.webroot_map) + def test_domain_before_webroot_error(self): + self.assertRaises(errors.PluginError, self.parser.parse_args, + "-d foo -w bar -w baz".split()) + self.assertRaises(errors.PluginError, self.parser.parse_args, + "-d foo -w bar -d baz -w qux".split()) + if __name__ == "__main__": unittest.main() # pragma: no cover From 7ac614e763a390ffcb83c5e58d3281267bb62943 Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Mon, 4 Apr 2016 16:48:15 -0400 Subject: [PATCH 175/317] Update pipstrap to 1.1.1. Report crashes without crashing under Python 2.6. --- letsencrypt-auto-source/pieces/pipstrap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-auto-source/pieces/pipstrap.py b/letsencrypt-auto-source/pieces/pipstrap.py index 016f7ca13..505f8ca72 100755 --- a/letsencrypt-auto-source/pieces/pipstrap.py +++ b/letsencrypt-auto-source/pieces/pipstrap.py @@ -41,7 +41,7 @@ except ImportError: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] - raise CalledProcessError(retcode, cmd, output=output) + raise CalledProcessError(retcode, cmd) return output from sys import exit, version_info from tempfile import mkdtemp @@ -55,7 +55,7 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 0 +__version__ = 1, 1, 1 # wheel has a conditional dependency on argparse: From 68e17604cdf8239b7709934ac6c08d3b3805ee40 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:56:32 -0700 Subject: [PATCH 176/317] make separate action testing class --- letsencrypt/plugins/webroot_test.py | 33 ++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 155f1cf51..2609d6ed9 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -45,11 +45,6 @@ class AuthenticatorTest(unittest.TestCase): webroot_map={"thing.com": self.path}) self.auth = Authenticator(self.config, "webroot") - self.parser = argparse.ArgumentParser() - self.parser.add_argument("-d", "--domains", - action="append", default=[]) - self.auth.inject_parser_options(self.parser, self.auth.name) - def tearDown(self): shutil.rmtree(self.path) @@ -230,18 +225,31 @@ class AuthenticatorTest(unittest.TestCase): self.assertFalse(os.path.exists(self.validation_path)) self.assertTrue(os.path.exists(self.root_challenge_path)) + +class WebrootActionTest(unittest.TestCase): + """Tests for webroot argparse actions.""" + + achall = achallenges.KeyAuthorizationAnnotatedChallenge( + challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) + + def setUp(self): + from letsencrypt.plugins.webroot import Authenticator + self.path = tempfile.mkdtemp() + self.parser = argparse.ArgumentParser() + self.parser.add_argument("-d", "--domains", + action="append", default=[]) + Authenticator.inject_parser_options(self.parser, "webroot") + def test_webroot_map_action(self): args = self.parser.parse_args( ["--webroot-map", '{{"thing.com":"{0}"}}'.format(self.path)]) - self.assertEqual(args.webroot_map, self.config.webroot_map) + self.assertEqual(args.webroot_map["thing.com"], self.path) def test_domain_before_webroot(self): args = self.parser.parse_args( "-d {0} -w {1}".format(self.achall.domain, self.path).split()) - self.auth.config = args - self.auth.perform([self.achall]) - self.assertEqual(self.auth.config.webroot_map, - self.config.webroot_map) + config = self._get_config_after_perform(args) + self.assertEqual(config.webroot_map[self.achall.domain], self.path) def test_domain_before_webroot_error(self): self.assertRaises(errors.PluginError, self.parser.parse_args, @@ -249,6 +257,11 @@ class AuthenticatorTest(unittest.TestCase): self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -d baz -w qux".split()) + def _get_config_after_perform(self, config): + from letsencrypt.plugins.webroot import Authenticator + auth = Authenticator(config, "webroot") + auth.perform([self.achall]) + return auth.config if __name__ == "__main__": unittest.main() # pragma: no cover From aab1a080ce4b319c14e7226ff73bf3223be798c4 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 13:58:55 -0700 Subject: [PATCH 177/317] test multiwebroot --- letsencrypt/plugins/webroot_test.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/letsencrypt/plugins/webroot_test.py b/letsencrypt/plugins/webroot_test.py index 2609d6ed9..f7ed7fdbf 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/letsencrypt/plugins/webroot_test.py @@ -257,11 +257,20 @@ class WebrootActionTest(unittest.TestCase): self.assertRaises(errors.PluginError, self.parser.parse_args, "-d foo -w bar -d baz -w qux".split()) + def test_multiwebroot(self): + args = self.parser.parse_args("-w {0} -d {1} -w {2} -d bar".format( + self.path, self.achall.domain, tempfile.mkdtemp()).split()) + self.assertEqual(args.webroot_map[self.achall.domain], self.path) + config = self._get_config_after_perform(args) + self.assertEqual( + config.webroot_map[self.achall.domain], self.path) + def _get_config_after_perform(self, config): from letsencrypt.plugins.webroot import Authenticator auth = Authenticator(config, "webroot") auth.perform([self.achall]) return auth.config + if __name__ == "__main__": unittest.main() # pragma: no cover From 4505b68a9a91152c1c9c54c614465f7e7f091f08 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 14:05:46 -0700 Subject: [PATCH 178/317] put generator expression on one line --- letsencrypt/plugins/webroot.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 24d8d3c1c..621df3909 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -249,10 +249,9 @@ class _WebrootMapAction(argparse.Action): def __call__(self, parser, namespace, webroot_map, option_string=None): for domains, webroot_path in six.iteritems(json.loads(webroot_map)): - validated_webroot_path = _validate_webroot(webroot_path) + webroot_path = _validate_webroot(webroot_path) namespace.webroot_map.update( - (d, validated_webroot_path,) - for d in cli.add_domains(namespace, domains)) + (d, webroot_path) for d in cli.add_domains(namespace, domains)) class _WebrootPathAction(argparse.Action): From 558806e2b710495c8de0108b6a2b9c40effcbcb2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 14:26:07 -0700 Subject: [PATCH 179/317] add cli_flag for noninteractive --- letsencrypt/plugins/webroot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 621df3909..4331031da 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -115,7 +115,7 @@ to serve all files under specified web root ({0}).""" code, index = display.menu( "Select the webroot for {0}:".format(domain), ["Enter a new webroot"] + known_webroots, - help_label="Help") + help_label="Help", cli_flag="--" + self.option_name("path")) if code == display_util.CANCEL: raise errors.PluginError( "Every requested domain must have a " From 6e82ce841a11a4e4986a1bd5b1d60558c5e19aac Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 15:29:49 -0700 Subject: [PATCH 180/317] properly set --agree-tos with --dry-run --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 7058b7efe..dfce13a0c 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -339,7 +339,7 @@ class HelpfulArgumentParser(object): if glob.glob(os.path.join(parsed_args.config_dir, constants.ACCOUNTS_DIR, "*")): # The user has a prod account, but might not have a staging # one; we don't want to start trying to perform interactive registration - parsed_args.agree_tos = True + parsed_args.tos = True parsed_args.register_unsafely_without_email = True if parsed_args.csr: From d1971233868bb70eb385b4db4de802f3b39bfa56 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 15:51:56 -0700 Subject: [PATCH 181/317] dry_run_tests++ --- letsencrypt/tests/cli_test.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 881f52e8f..244ec38ad 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -427,20 +427,40 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods short_args += '--server example.com'.split() self._check_server_conflict_message(short_args, '--staging') - def _assert_dry_run_flag_worked(self, namespace): + def _assert_dry_run_flag_worked(self, namespace, existing_account): self.assertTrue(namespace.dry_run) self.assertTrue(namespace.break_my_certs) self.assertTrue(namespace.staging) self.assertEqual(namespace.server, constants.STAGING_URI) + if existing_account: + self.assertTrue(namespace.tos) + self.assertTrue(namespace.register_unsafely_without_email) + else: + self.assertFalse(namespace.tos) + self.assertFalse(namespace.register_unsafely_without_email) + def test_dry_run_flag(self): parse = self._get_argument_parser() - short_args = ['--dry-run'] + config_dir = tempfile.mkdtemp() + short_args = '--dry-run --config-dir {0}'.format(config_dir).split() self.assertRaises(errors.Error, parse, short_args) - self._assert_dry_run_flag_worked(parse(short_args + ['auth'])) + self._assert_dry_run_flag_worked( + parse(short_args + ['auth']), False) + self._assert_dry_run_flag_worked( + parse(short_args + ['certonly']), False) + self._assert_dry_run_flag_worked( + parse(short_args + ['renew']), False) + + account_dir = os.path.join(config_dir, constants.ACCOUNTS_DIR) + os.mkdir(account_dir) + os.mkdir(os.path.join(account_dir, 'fake_account_dir')) + + self._assert_dry_run_flag_worked(parse(short_args + ['auth']), True) + self._assert_dry_run_flag_worked(parse(short_args + ['renew']), True) short_args += ['certonly'] - self._assert_dry_run_flag_worked(parse(short_args)) + self._assert_dry_run_flag_worked(parse(short_args), True) short_args += '--server example.com'.split() conflicts = ['--dry-run'] From f93f1e5695bd2cea2c916025445ae357128524b5 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 15:58:10 -0700 Subject: [PATCH 182/317] Remove needless unsafe registration warnings --- letsencrypt/client.py | 3 ++- letsencrypt/tests/client_test.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index da2e1f086..221879080 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -105,7 +105,8 @@ def register(config, account_storage, tos_cb=None): "--register-unsafely-without-email was not present.") logger.warn(msg) raise errors.Error(msg) - logger.warn("Registering without email!") + if not config.dry_run: + logger.warn("Registering without email!") # Each new registration shall use a fresh new key key = jose.JWKRSA(key=jose.ComparableRSAKey( diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index ed4e5def0..ec2d46ff7 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -80,6 +80,7 @@ class RegisterTest(unittest.TestCase): with mock.patch("letsencrypt.account.report_new_account"): self.config.email = None self.config.register_unsafely_without_email = True + self.config.dry_run = False self._call() mock_logger.warn.assert_called_once_with(mock.ANY) From 23dfb5b99ea206a38f96cd099b865df4cebab819 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 16:09:45 -0700 Subject: [PATCH 183/317] Remove unecessary if --- letsencrypt/cli.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 7058b7efe..345c6152b 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -325,9 +325,8 @@ class HelpfulArgumentParser(object): if parsed_args.server not in (flag_default("server"), constants.STAGING_URI): conflicts = ["--staging"] if parsed_args.staging else [] conflicts += ["--dry-run"] if parsed_args.dry_run else [] - if not self.detect_defaults: - raise errors.Error("--server value conflicts with {0}".format( - " and ".join(conflicts))) + raise errors.Error("--server value conflicts with {0}".format( + " and ".join(conflicts))) parsed_args.server = constants.STAGING_URI From 237adfdce2711c3eee6609b129b166babb3fb029 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 4 Apr 2016 16:19:41 -0700 Subject: [PATCH 184/317] I was told to cleanup after myself --- letsencrypt/plugins/webroot.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 4331031da..4e3b8099c 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -221,11 +221,12 @@ to serve all files under specified web root ({0}).""" def cleanup(self, achalls): # pylint: disable=missing-docstring for achall in achalls: - root_path = self.full_roots[achall.domain] - validation_path = self._get_validation_path(root_path, achall) - logger.debug("Removing %s", validation_path) - os.remove(validation_path) - self.performed[root_path].remove(achall) + root_path = self.full_roots.get(achall.domain, None) + if root_path is not None: + validation_path = self._get_validation_path(root_path, achall) + logger.debug("Removing %s", validation_path) + os.remove(validation_path) + self.performed[root_path].remove(achall) for root_path, achalls in six.iteritems(self.performed): if not achalls: From 889fc327d25436c5a81b029fae1937e15a438a0e Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 4 Apr 2016 16:34:39 -0700 Subject: [PATCH 185/317] Certonly & no action: print a note explaining why no action was taken --- letsencrypt/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/letsencrypt/main.py b/letsencrypt/main.py index ac0ec69e8..0fb56179d 100644 --- a/letsencrypt/main.py +++ b/letsencrypt/main.py @@ -531,6 +531,8 @@ def obtain_cert(config, plugins, lineage=None): installer.restart() notify("new certificate deployed with reload of {0} server; fullchain is {1}".format( config.installer, lineage.fullchain), pause=False) + elif action == "reinstall" and config.verb == "certonly": + notify("Certificate not yet due for renewal; no action taken.") _suggest_donation_if_appropriate(config, action) From cae9a81a8c6b7fecc4b18bbbbc3161d1000aeb87 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 4 Apr 2016 16:39:24 -0700 Subject: [PATCH 186/317] Add boulder-mysql and boulder-rabbitmq hosts. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6b325e985..8e7021e34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,6 +73,8 @@ addons: - le2.wtf - le3.wtf - nginx.wtf + - boulder-mysql + - boulder-rabbitmq mariadb: "10.0" apt: sources: From a5073b28c3e979b1f2c0722d2730507129b07099 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 5 Apr 2016 10:58:42 -0700 Subject: [PATCH 187/317] New description of webroot for the UI --- letsencrypt/plugins/webroot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/plugins/webroot.py b/letsencrypt/plugins/webroot.py index 4e3b8099c..a0f7ef9c3 100644 --- a/letsencrypt/plugins/webroot.py +++ b/letsencrypt/plugins/webroot.py @@ -27,7 +27,7 @@ logger = logging.getLogger(__name__) class Authenticator(common.Plugin): """Webroot Authenticator.""" - description = "Webroot Authenticator" + description = "Place files in webroot directory" MORE_INFO = """\ Authenticator plugin that performs http-01 challenge by saving From b0c34b7581507c25c821a4dd787109cec44f4591 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 5 Apr 2016 16:34:42 -0700 Subject: [PATCH 188/317] build le-auto --- letsencrypt-auto-source/letsencrypt-auto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 79ce3d3cb..2adaf0375 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -700,7 +700,7 @@ except ImportError: cmd = kwargs.get("args") if cmd is None: cmd = popenargs[0] - raise CalledProcessError(retcode, cmd, output=output) + raise CalledProcessError(retcode, cmd) return output from sys import exit, version_info from tempfile import mkdtemp @@ -714,7 +714,7 @@ except ImportError: from urllib.parse import urlparse # 3.4 -__version__ = 1, 1, 0 +__version__ = 1, 1, 1 # wheel has a conditional dependency on argparse: From ee2ef9a345f859d3f76b54724b14fa478ddaf2ff Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 5 Apr 2016 16:36:11 -0700 Subject: [PATCH 189/317] reuse venv sometimes on Python 2.6 --- letsencrypt-auto-source/letsencrypt-auto | 3 ++- letsencrypt-auto-source/letsencrypt-auto.template | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 2adaf0375..98e6d8e65 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -445,7 +445,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) else INSTALLED_VERSION="none" fi diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 40edca7fe..526a03fae 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -176,7 +176,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) else INSTALLED_VERSION="none" fi From 4d4cfb414f2847f1814cfe2949c6e76f112e4ade Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 5 Apr 2016 17:55:18 -0700 Subject: [PATCH 190/317] Release 0.5.0 --- acme/setup.py | 2 +- letsencrypt-apache/setup.py | 2 +- letsencrypt-auto | 1507 ++++------------- letsencrypt-auto-source/letsencrypt-auto | 20 +- letsencrypt-auto-source/letsencrypt-auto.sig | Bin 256 -> 256 bytes .../pieces/letsencrypt-auto-requirements.txt | 18 +- letsencrypt-compatibility-test/setup.py | 2 +- letsencrypt-nginx/setup.py | 2 +- letsencrypt/__init__.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- 10 files changed, 355 insertions(+), 1202 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 0843288e6..4845d7f1e 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.5.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 46f4da54c..93b0f3296 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.5.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/letsencrypt-auto b/letsencrypt-auto index 8dda5f183..942fd8ea2 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.4.2" +LE_AUTO_VERSION="0.5.0" # This script takes the same arguments as the main letsencrypt program, but it # additionally responds to --verbose (more output) and --debug (allow support @@ -348,28 +348,45 @@ BootstrapFreeBsd() { } BootstrapMac() { - if ! hash brew 2>/dev/null; then - echo "Homebrew not installed.\nDownloading..." - ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" fi - if [ -z "$(brew list --versions augeas)" ]; then - echo "augeas not installed.\nInstalling augeas from Homebrew..." - brew install augeas + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python fi - if [ -z "$(brew list --versions dialog)" ]; then - echo "dialog not installed.\nInstalling dialog from Homebrew..." - brew install dialog + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib fi - if [ -z "$(brew list --versions python)" ]; then - echo "python not installed.\nInstalling python from Homebrew..." - brew install python + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python fi if ! hash virtualenv 2>/dev/null; then - echo "virtualenv not installed.\nInstalling with pip..." + echo "virtualenv not installed." + echo "Installing with pip..." pip install virtualenv fi } @@ -411,7 +428,7 @@ Bootstrap() { 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 peep install manually." + echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" echo "for more info." fi @@ -421,19 +438,6 @@ TempDir() { mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || OS X } -InstallRequirements() { - set +e - PEEP_OUT=`"$VENV_BIN/python" "$TEMP_DIR/peep.py" install -r "$TEMP_DIR/$1"` - PEEP_STATUS=$? - set -e - if [ "$PEEP_STATUS" != 0 ]; then - # Report error. (Otherwise, be quiet.) - echo "Had a problem while downloading and verifying Python packages:" - echo "$PEEP_OUT" - rm -rf "$VENV_PATH" - exit 1 - fi -} if [ "$1" = "--le-auto-phase2" ]; then @@ -441,7 +445,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -457,255 +462,214 @@ if [ "$1" = "--le-auto-phase2" ]; then echo "Installing Python packages..." TEMP_DIR=$(TempDir) - trap "rm -rf '$TEMP_DIR'" EXIT # There is no $ interpolation due to quotes on starting heredoc delimiter. - # ------------------------------------------------------------------------- - cat << "UNLIKELY_EOF" > "$TEMP_DIR/setuptools-requirements.txt" -# cryptography requires a more modern version of setuptools. -# sha256: _ANFf7h6utSdwJ-cMTOGNpPn3bbKgrtQpzmnc3nOWpo -# sha256: JPz8FTZKn-CaIg830tztyEl5Xj3j5LOT7piOZqnL2Fo -# sha256: gJaELiTE8ddN_xKr6Qwm0S8F0NmlbtXgb8qm-qHkC2o -setuptools==20.2.2 - -UNLIKELY_EOF # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" # This is the flattened list of packages letsencrypt-auto installs. To generate # this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and # then use `hashin` or a more secure method to gather the hashes. -# sha256: wxZH7baf09RlqEfqMVfTe-0flfGXYLEaR6qRwEtmYxQ -# sha256: YrCJpVvh2JSc0rx-DfC9254Cj678jDIDjMhIYq791uQ -argparse==1.4.0 +argparse==1.4.0 \ + --hash=sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314 \ + --hash=sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4 # This comes before cffi because cffi will otherwise install an unchecked # version via setup_requires. -# sha256: eVm0p0q9wnsxL-0cIebK-TCc4LKeqGtZH9Lpns3yf3M -pycparser==2.14 +pycparser==2.14 \ + --hash=sha256:7959b4a74abdc27b312fed1c21e6caf9309ce0b29ea86b591fd2e99ecdf27f73 -# sha256: U8HJ3bMEMVE-t_PN7wo-BrDxJSGIqqd0SvD1pM1F268 -# sha256: pWj0nfyhKo2fNwGHJX78WKOBCeHu5xTZKFYdegGKZPg -# sha256: gJxsqM-8ruv71DK0V2ABtA04_yRjdzy1dXfXXhoCC8M -# sha256: hs3KLNnLpBQiIwOQ3xff6qnzRKkR45dci-naV7NVSOk -# sha256: JLE9uErsOFyiPHuN7YPvi7QXe8GB0UdY-fl1vl0CDYY -# sha256: lprv_XwOCX9r4e_WgsFWriJlkaB5OpS2wtXkKT9MjU4 -# sha256: AA81jUsPokn-qrnBzn1bL-fgLnvfaAbCZBhQX8aF4mg -# sha256: qdhvRgu9g1ii1ROtd54_P8h447k6ALUAL66_YW_-a5w -# sha256: MSezqzPrI8ysBx-aCAJ0jlz3xcvNAkgrsGPjW0HbsLA -# sha256: 4rLUIjZGmkAiTTnntsYFdfOIsvQj81TH7pClt_WMgGU -# sha256: jC3Mr-6JsbQksL7GrS3ZYiyUnSAk6Sn12h7YAerHXx0 -# sha256: pN56TRGu1Ii6tPsU9JiFh6gpvs5aIEM_eA1uM7CAg8s -# sha256: XKj-MEJSZaSSdOSwITobyY9LE0Sa5elvmEdx5dg-WME -# sha256: pP04gC9Z5xTrqBoCT2LbcQsn2-J6fqEukRU3MnqoTTA -# sha256: hs1pErvIPpQF1Kc81_S07oNTZS0tvHyCAQbtW00bqzo -# sha256: jx0XfTZOo1kAQVriTKPkcb49UzTtBBkpQGjEn0WROZg -cffi==1.4.2 +cffi==1.4.2 \ + --hash=sha256:53c1c9ddb30431513eb7f3cdef0a3e06b0f1252188aaa7744af0f5a4cd45dbaf \ + --hash=sha256:a568f49dfca12a8d9f370187257efc58a38109e1eee714d928561d7a018a64f8 \ + --hash=sha256:809c6ca8cfbcaeebfbd432b4576001b40d38ff2463773cb57577d75e1a020bc3 \ + --hash=sha256:86cdca2cd9cba41422230390df17dfeaa9f344a911e3975c8be9da57b35548e9 \ + --hash=sha256:24b13db84aec385ca23c7b8ded83ef8bb4177bc181d14758f9f975be5d020d86 \ + --hash=sha256:969aeffd7c0e097f6be1efd682c156ae226591a0793a94b6c2d5e4293f4c8d4e \ + --hash=sha256:000f358d4b0fa249feaab9c1ce7d5b2fe7e02e7bdf6806c26418505fc685e268 \ + --hash=sha256:a9d86f460bbd8358a2d513ad779e3f3fc878e3b93a00b5002faebf616ffe6b9c \ + --hash=sha256:3127b3ab33eb23ccac071f9a0802748e5cf7c5cbcd02482bb063e35b41dbb0b0 \ + --hash=sha256:e2b2d42236469a40224d39e7b6c60575f388b2f423f354c7ee90a5b7f58c8065 \ + --hash=sha256:8c2dccafee89b1b424b0bec6ad2dd9622c949d2024e929f5da1ed801eac75f1d \ + --hash=sha256:a4de7a4d11aed488bab4fb14f4988587a829bece5a20433f780d6e33b08083cb \ + --hash=sha256:5ca8fe30425265a49274e4b0213a1bc98f4b13449ae5e96f984771e5d83e58c1 \ + --hash=sha256:a4fd38802f59e714eba81a024f62db710b27dbe27a7ea12e911537327aa84d30 \ + --hash=sha256:86cd6912bbc83e9405d4a73cd7f4b4ee8353652d2dbc7c820106ed5b4d1bab3a \ + --hash=sha256:8f1d177d364ea35900415ae24ca3e471be3d5334ed0419294068c49f45913998 +ConfigArgParse==0.10.0 \ + --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +configobj==5.0.6 \ + --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 +cryptography==1.2.3 \ + --hash=sha256:031938f73a5c5eb3e809e18ff7caeb6865351871417be6050cb8c86a9a202b9a \ + --hash=sha256:a179a38d50f8d68b491d7a313db78f8cabe290842cecddddc7b34d408e59db0a \ + --hash=sha256:906c88b2aadcf99cfabb24098263d1bf65ab0c8688acde10dae1f09d865920f1 \ + --hash=sha256:6e706c5c6088770b1d1b634e959e21963e315b0255f5f4777125ad3d54082977 \ + --hash=sha256:f5ebf8e31c48f8707921dca0e994de77813a9c9b9bf03c119c5ddf97bdcffe73 \ + --hash=sha256:c7b89e42288cc7fbee3812e99ef5c744f22452e11d6822f6807afc6d6b3be83e \ + --hash=sha256:8408d29865947109d8b68f1837a7cde1aa4dc86e0f79ca3ba58c0c44e443d6a5 \ + --hash=sha256:c7e76cf3c3d925dd31fa238cfb806cffba718c0f08707d77a538768477969956 \ + --hash=sha256:7d8de35380f31702758b7753bb5c40723832c73006dedb2f9099bf61a37f7287 \ + --hash=sha256:5edbee71fae5469ee83fe0a37866b9398c8ce3a46325c24fcedfbf097bb48a19 \ + --hash=sha256:594edafe4801c13bdc1cc305e7704a90c19617e95936f6ab457ee4ffe000ba50 \ + --hash=sha256:b7fdb16a0a7f481be42da744bfe1ea2163025de21f90f2c688a316f3c354da9c \ + --hash=sha256:207b8bf0fe0907336df38b733b487521cf9e138189aba9234ad54fe545dd0db8 \ + --hash=sha256:509a2f05386270cf783993c90d49ffefb3dd62aee45bf1ea8ce3d2cde7271c21 \ + --hash=sha256:ac69b65dd1af0179ede40c9f15788c88f73e628ea6c0519de3838e279bb388c6 \ + --hash=sha256:8df6fad6c6ae12fd7004ea29357f0a2b4d3774eaeca7656530d08d2d90cd41aa \ + --hash=sha256:0b8b96dd81cc1533a04f30382c0fe21c1972e189f794d0c4261a18cec08fd9b5 \ + --hash=sha256:cae8fca1883f23c50ea78d89de6fe4fefdb4cea83177760f47177559414ded93 \ + --hash=sha256:1a471ca576a9cdce1b1cd9f3a22b1d09ee44d46862037557de17919c0db44425 \ + --hash=sha256:8ec4e8e3d453b3a1b63b5f57737a434dcf1ee4a2f26f6ff7c5a37c3f679104d2 \ + --hash=sha256:8eb11c77dd8e73f48df6b2f7a7e16173fe0fe8fdfe266232832e88477e08454e +enum34==1.1.2 \ + --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ + --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 +funcsigs==0.4 \ + --hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \ + --hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033 +idna==2.0 \ + --hash=sha256:9b2fc50bd3c4ba306b9651b69411ef22026d4d8335b93afc2214cef1246ce707 \ + --hash=sha256:16199aad938b290f5be1057c0e1efc6546229391c23cea61ca940c115f7d3d3b +ipaddress==1.0.16 \ + --hash=sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5 \ + --hash=sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0 +linecache2==1.0.0 \ + --hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \ + --hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c +ndg-httpsclient==0.4.0 \ + --hash=sha256:e8c155fdebd9c4bcb0810b4ed01ae1987554b1ee034dd7532d7b8fdae38a6274 +ordereddict==1.1 \ + --hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f +parsedatetime==2.1 \ + --hash=sha256:ce9d422165cf6e963905cd5f74f274ebf7cc98c941916169178ef93f0e557838 \ + --hash=sha256:17c578775520c99131634e09cfca5a05ea9e1bd2a05cd06967ebece10df7af2d +pbr==1.8.1 \ + --hash=sha256:46c8db75ae75a056bd1cc07fa21734fe2e603d11a07833ecc1eeb74c35c72e0c \ + --hash=sha256:e2127626a91e6c885db89668976db31020f0af2da728924b56480fc7ccf09649 +psutil==3.3.0 \ + --hash=sha256:584f0b29fcc5d523b433cb8918b2fc74d67e30ee0b44a95baf031528f424619f \ + --hash=sha256:28ca0b6e9d99aa8dc286e8747a4471362b69812a25291de29b6a8d70a1545a0d \ + --hash=sha256:167ad5fff52a672c4ddc1c1a0b25146d6813ebb08a9aab0a3ac45f8a5b669c3b \ + --hash=sha256:e6dea6173a988727bb223d3497349ad5cdef5c0b282eff2d83e5f9065c53f85f \ + --hash=sha256:2af5e0a4aad66049955d0734aa4e3dc8caa17a9eaf8b4c1a27a5f1ee6e40f6fc \ + --hash=sha256:d9884dc0dc2e55e2448e495778dc9899c1c8bf37aeb2f434c1bea74af93c2683 \ + --hash=sha256:e27c2fe6dfcc8738be3d2c5a022f785eb72971057e1a9e1e34fba73bce8a71a6 \ + --hash=sha256:65afd6fecc8f3aed09ee4be63583bc8eb472f06ceaa4fe24c4d1d5a1a3c0e13f \ + --hash=sha256:ba1c558fbfcdf94515c2394b1155c1dc56e2bc2a9c17d30349827c9ed8a67e46 \ + --hash=sha256:ba95ea0022dcb64d36f0c1335c0605fae35bdf3e0fea8d92f5d0f6456a35e55b \ + --hash=sha256:421b6591d16b509aaa8d8c15821d66bb94cb4a8dc4385cad5c51b85d4a096d85 \ + --hash=sha256:326b305cbdb6f94dafbfe2c26b11da88b0ab07b8a07f8188ab9d75ff0c6e841a \ + --hash=sha256:9aede5b2b6fe46b3748ea8e5214443890d1634027bef3d33b7dad16556830278 \ + --hash=sha256:73bed1db894d1aa9c3c7e611d302cdeab7ae8a0dc0eeaf76727878db1ac5cd87 \ + --hash=sha256:935b5dd6d558af512f42501a7c08f41d7aff139af1bb3959daa3abb859234d6c \ + --hash=sha256:4ca0111cf157dcc0f2f69a323c5b5478718d68d45fc9435d84be0ec0f186215b \ + --hash=sha256:b6f13c95398a3fcf0226c4dcfa448560ba5865259cd96ec2810658651e932189 \ + --hash=sha256:ee6be30d1635bbdea4c4325d507dc8a0dbbde7e1c198bd62ddb9f43198b9e214 \ + --hash=sha256:dfa786858c268d7fbbe1b6175e001ec02738d7cfae0a7ce77bf9b651af676729 \ + --hash=sha256:aa77f9de72af9c16cc288cd4a24cf58824388f57d7a81e400c4616457629870e \ + --hash=sha256:f500093357d04da8140d87932cac2e54ef592a54ca8a743abb2850f60c2c22eb +pyasn1==0.1.9 \ + --hash=sha256:61f9d99e3cef65feb1bfe3a2eef7a93eb93819d345bf54bcd42f4e63d5204dae \ + --hash=sha256:1802a6dd32045e472a419db1441aecab469d33e0d2749e192abdec52101724af \ + --hash=sha256:35025cd9422c96504912f04e2f15fe79390a8597b430c2ca5d0534cf9309ffa0 \ + --hash=sha256:2f96ed5a0c329ca16230b326ca12b7461ec8f65e0be3e4f997516f36bf82a345 \ + --hash=sha256:28fee44217991cfad9e6a0b9f7e3f26041e21ebc96629e94e585ccd05d49fa65 \ + --hash=sha256:326e7a854a17fab07691204747695f8f692d674588a355c441fb14f660bf4e68 \ + --hash=sha256:cda5a90485709ca6795c86056c3e5fe7266028b05e53f1d527fdf93a6365a6b8 \ + --hash=sha256:0cb2a14742b543fdd68f931a14ce3829186ed2b1b2267a06787388c96b2dd9be \ + --hash=sha256:5191ff6b9126d2c039dd87f8ff025bed274baf07fa78afa46f556b1ad7265d6e \ + --hash=sha256:8323e03637b2d072cc7041300bac6ec448c3c28950ab40376036788e9a1af629 \ + --hash=sha256:853cacd96d1f701ddd67aa03ecc05f51890135b7262e922710112f12a2ed2a7f +pyOpenSSL==0.15.1 \ + --hash=sha256:88e45e6bb25dfed272a1ef2e728461d44b634c2cd689e989b6e56a349c5a3ae5 \ + --hash=sha256:f0a26070d6db0881de8bcc7846934b7c3c930d8f9c79d45883ee48984bc0d672 +pyRFC3339==1.0 \ + --hash=sha256:eea31835c56e2096af4363a5745a784878a61d043e247d3a6d6a0a32a9741f56 \ + --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 +python-augeas==0.5.0 \ + --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 +python2-pythondialog==3.3.0 \ + --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ + --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa +pytz==2015.7 \ + --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ + --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ + --hash=sha256:ead4aefa7007249e05e51b01095719d5a8dd95760089f5730aac5698b1932918 \ + --hash=sha256:3cca0df08bd0ed98432390494ce3ded003f5e661aa460be7a734bffe35983605 \ + --hash=sha256:3ede470d3d17ba3c07638dfa0d10452bc1b6e5ad326127a65ba77e6aaeb11bec \ + --hash=sha256:68c47964f7186eec306b13629627722b9079cd4447ed9e5ecaecd4eac84ca734 \ + --hash=sha256:dd5d3991950aae40a6c81de1578942e73d629808cefc51d12cd157980e6cfc18 \ + --hash=sha256:a77c52062c07eb7c7b30545dbc73e32995b7e117eea750317b5cb5c7a4618f14 \ + --hash=sha256:81af9aec4bc960a9a0127c488f18772dae4634689233f06f65443e7b11ebeb51 \ + --hash=sha256:e079b1dadc5c06246cc1bb6fe1b23a50b1d1173f2edd5104efd40bb73a28f406 \ + --hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \ + --hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \ + --hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3 +requests==2.9.1 \ + --hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \ + --hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f +six==1.10.0 \ + --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \ + --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a +traceback2==1.4.0 \ + --hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23 \ + --hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 +unittest2==1.1.0 \ + --hash=sha256:13f77d0875db6d9b435e1d4f41e74ad4cc2eb6e1d5c824996092b3430f088bb8 \ + --hash=sha256:22882a0e418c284e1f718a822b3b022944d53d2d908e1690b319a9d3eb2c0579 +zope.component==4.2.2 \ + --hash=sha256:282c112b55dd8e3c869a3571f86767c150ab1284a9ace2bdec226c592acaf81a +zope.event==4.1.0 \ + --hash=sha256:dc7a59a2fd91730d3793131a5d261b29e93ec4e2a97f1bc487ce8defee2fe786 +zope.interface==4.1.3 \ + --hash=sha256:f07b631f7a601cd8cbd3332d54f43142c7088a83299f859356f08d1d4d4259b3 \ + --hash=sha256:de5cca083b9439d8002fb76bbe6b4998c5a5a721fab25b84298967f002df4c94 \ + --hash=sha256:6788416f7ea7f5b8a97be94825377aa25e8bdc73463e07baaf9858b29e737077 \ + --hash=sha256:6f3230f7254518201e5a3708cbb2de98c848304f06e3ded8bfb39e5825cba2e1 \ + --hash=sha256:5fa575a5240f04200c3088427d0d4b7b737f6e9018818a51d8d0f927a6a2517a \ + --hash=sha256:522194ad6a545735edd75c8a83f48d65d1af064e432a7d320d64f56bafc12e99 \ + --hash=sha256:e8c7b2d40943f71c99148c97f66caa7f5134147f57423f8db5b4825099ce9a09 \ + --hash=sha256:279024f0208601c3caa907c53876e37ad88625f7eaf1cb3842dbe360b2287017 \ + --hash=sha256:2e221a9eec7ccc58889a278ea13dcfed5ef939d80b07819a9a8b3cb1c681484f \ + --hash=sha256:69118965410ec86d44dc6b9017ee3ddbd582e0c0abeef62b3a19dbf6c8ad132b \ + --hash=sha256:d04df8686ec864d0cade8cf199f7f83aecd416109a20834d568f8310ded12dea \ + --hash=sha256:e75a947e15ee97e7e71e02ea302feb2fc62d3a2bb4668bf9dfbed43a506ac7e7 \ + --hash=sha256:4e45d22fb883222a5ab9f282a116fec5ee2e8d1a568ccff6a2d75bbd0eb6bcfc \ + --hash=sha256:bce9339bb3c7a55e0803b63d21c5839e8e479bc85c4adf42ae415b72f94facb2 \ + --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ + --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ + --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 +mock==1.0.1 \ + --hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \ + --hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc -# sha256: O1CoPdWBSd_O6Yy2VlJl0QtT6cCivKfu73-19VJIkKc -ConfigArgParse==0.10.0 +# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -# sha256: ovVlB3DhyH-zNa8Zqbfrc_wFzPIhROto230AzSvLCQI -configobj==5.0.6 - -# sha256: Axk49zpcXrPoCeGP98rraGU1GHFBe-YFDLjIapogK5o -# sha256: oXmjjVD41otJHXoxPbePjKvikIQs7N3dx7NNQI5Z2wo -# sha256: kGyIsqrc-Zz6uyQJgmPRv2WrDIaIrN4Q2uHwnYZZIPE -# sha256: bnBsXGCIdwsdG2NOlZ4hlj4xWwJV9fR3cSWtPVQIKXc -# sha256: 9ev44xxI-HB5Idyg6ZTed4E6nJub8DwRnF3fl73P_nM -# sha256: x7ieQiiMx_vuOBLpnvXHRPIkUuEdaCL2gHr8bWs76D4 -# sha256: hAjSmGWUcQnYto8YN6fN4apNyG4Peco7pYwMRORD1qU -# sha256: x-ds88PZJd0x-iOM-4Bs_7pxjA8IcH13pTh2hHeWmVY -# sha256: fY3jU4DzFwJ1i3dTu1xAcjgyxzAG3tsvkJm_YaN_coc -# sha256: XtvucfrlRp7oP-CjeGa5OYyM46RjJcJPzt-_CXu0ihk -# sha256: WU7a_kgBwTvcHMMF53BKkMGWF-lZNvarRX7k_-AAulA -# sha256: t_2xagp_SBvkLadEv-HqIWMCXeIfkPLGiKMW88NU2pw -# sha256: IHuL8P4JBzNt84tzO0h1Ic-eE4GJq6kjStVP5UXdDbg -# sha256: UJovBThicM94OZPJDUn_77PdYq7kW_HqjOPSzecnHCE -# sha256: rGm2XdGvAXnt5AyfFXiMiPc-Yo6mwFGd44OOJ5uziMY -# sha256: jfb61sauEv1wBOopNX8KK003dOrsp2VlMNCNLZDNQao -# sha256: C4uW3YHMFTOgTzA4LA_iHBly4Yn3lNDEJhoYzsCP2bU -# sha256: yuj8oYg_I8UOp42J3m_k_v20zqgxd3YPRxd1WUFN7ZM -# sha256: GkccpXapzc4bHNnzoisdCe5E1GhiA3VX3heRnA20RCU -# sha256: jsTo49RTs6G2O19Xc3pDTc8e5KLyb2_3xaN8P2eRBNI -# sha256: jrEcd92Oc_SN9rL3p-Fhc_4P6P3-JmIygy6IR34IRU4 -cryptography==1.2.3 - -# sha256: JHXX_N31lR6S_1RpcnWIAt5SYL9Akxmp8ZNOa7yLHcc -# sha256: NZB977D5krdat3iPZf7cHPIP-iJojg5vbxKvwGs-pQE -enum34==1.1.2 - -# sha256: _1rZ4vjZ5dHou_vPR3IqtSfPDVHK7u2dptD0B5k4P94 -# sha256: 2Dzm3wsOpmGHAP4ds1NSY5Goo62ht6ulL-16Ydp3IDM -funcsigs==0.4 - -# sha256: my_FC9PEujBrllG2lBHvIgJtTYM1uTr8IhTO8SRs5wc -# sha256: FhmarZOLKQ9b4QV8Dh78ZUYik5HCPOphypQMEV99PTs -idna==2.0 - -# sha256: k1cSgAzkdgcB2JrWd2Zs1SaR_S9vCzQMi0I5o8F5iKU -# sha256: WjGCsyKnBlJcRigspvBk0noCz_vUSfn0dBbx3JaqcbA -ipaddress==1.0.16 - -# sha256: 54vpwKDfy6xxL-BPv5K5bN2ugLG4QvJCSCFMhJbwBu8 -# sha256: Syb_TnEQ23butvWntkqCYjg51ZXCA47tpmLyott46Xw -linecache2==1.0.0 - -# sha256: 6MFV_evZxLywgQtO0BrhmHVUse4DTddTLXuP2uOKYnQ -ndg-httpsclient==0.4.0 - -# sha256: HDW0rCBs7y0kgWyJ-Jzyid09OM98RJuz-re_bUPwGx8 -ordereddict==1.1 - -# sha256: zp1CIWXPbpY5Bc1fdPJ06_fMmMlBkWFpF475Pw5VeDg -# sha256: F8V4d1UgyZExY04Jz8paBeqeG9KgXNBpZ-vs4Q33ry0 -parsedatetime==2.1 - -# sha256: Rsjbda51oFa9HMB_ohc0_i5gPRGgeDPswe63TDXHLgw -# sha256: 4hJ2JqkebIhduJZol22zECDwry2nKJJLVkgPx8zwlkk -pbr==1.8.1 - -# sha256: WE8LKfzF1SO0M8uJGLL8dNZ-MO4LRKlbrwMVKPQkYZ8 -# sha256: KMoLbp2Zqo3Chuh0ekRxNitpgSolKR3im2qNcKFUWg0 -# sha256: FnrV__UqZyxN3BwaCyUUbWgT67CKmqsKOsRfiltmnDs -# sha256: 5t6mFzqYhye7Ij00lzSa1c3vXAsoLv8tg-X5BlxT-F8 -# sha256: KvXgpKrWYEmVXQc0qk49yMqhep6vi0waJ6Xx7m5A9vw -# sha256: 2YhNwNwuVeJEjklXeNyYmcHIvzeusvQ0wb6nSvk8JoM -# sha256: 4nwv5t_Mhzi-PSxaAi94XrcpcQV-Gp4eNPunO86KcaY -# sha256: Za_W_syPOu0J7kvmNYO8jrRy8GzqpP4kxNHVoaPA4T8 -# sha256: uhxVj7_N-UUVwjlLEVXB3FbivCqcF9MDSYJ8ntimfkY -# sha256: upXqACLctk028MEzXAYF-uNb3z4P6o2S9dD2RWo15Vs -# sha256: QhtlkdFrUJqqjYwVgh1mu5TLSo3EOFytXFG4XUoJbYU -# sha256: MmswXL22-U2vv-LCaxHaiLCrB7igf4GIq511_wxuhBo -# sha256: mu3lsrb-RrN0jqjlIURDiQ0WNAJ77z0zt9rRZVaDAng -# sha256: c77R24lNGqnDx-YR0wLN6reuig3A7q92cnh42xrFzYc -# sha256: k1td1tVYr1EvQlAafAj0HXr_E5rxuzlZ2qOruFkjTWw -# sha256: TKARHPFX3MDy9poyPFtUeHGNaNRfyUNdhL4OwPGGIVs -# sha256: tvE8lTmKP88CJsTc-kSFYLpYZSWc2W7CgQZYZR6TIYk -# sha256: 7mvjDRY1u96kxDJdUH3IoNu95-HBmL1i3bn0MZi54hQ -# sha256: 36eGhYwmjX-74bYXXgAewCc418-uCnzne_m2Ua9nZyk -# sha256: qnf53nKvnBbMKIzUokz1iCQ4j1fXqB5ADEYWRXYphw4 -# sha256: 9QAJM1fQTagUDYeTLKwuVO9ZKlTKinQ6uyhQ9gwsIus -psutil==3.3.0 - -# sha256: YfnZnjzvZf6xv-Oi7vepPrk4GdNFv1S81C9OY9UgTa4 -# sha256: GAKm3TIEXkcqQZ2xRBrsq0adM-DSdJ4ZKr3sUhAXJK8 -# sha256: NQJc2UIsllBJEvBOLxX-eTkKhZe0MMLKXQU0z5MJ_6A -# sha256: L5btWgwynKFiMLMmyhK3Rh7I9l4L4-T5l1FvNr-Co0U -# sha256: KP7kQheZHPrZ5qC59-PyYEHiHryWYp6U5YXM0F1J-mU -# sha256: Mm56hUoX-rB2kSBHR2lfj2ktZ0WIo1XEQfsU9mC_Tmg -# sha256: zaWpBIVwnKZ5XIYFbD5f5yZgKLBeU_HVJ_35OmNlprg -# sha256: DLKhR0K1Q_3Wj5MaFM44KRhu0rGyJnoGeHOIyWst2b4 -# sha256: UZH_a5Em0sA53Yf4_wJb7SdLrwf6eK-kb1VrGtcmXW4 -# sha256: gyPgNjey0HLMcEEwC6xuxEjDwolQq0A3YDZ4jpoa9ik -# sha256: hTys2W0fcB3dZ6oD7MBfUYkBNbcmLpInEBEvEqLtKn8 -pyasn1==0.1.9 - -# sha256: iORea7Jd_tJyoe8ucoRh1EtjTCzWiemJtuVqNJxaOuU -# sha256: 8KJgcNbbCIHei8x4RpNLfDyTDY-cedRYg-5ImEvA1nI -pyOpenSSL==0.15.1 - -# sha256: 7qMYNcVuIJavQ2OldFp4SHimHQQ-JH06bWoKMql0H1Y -# sha256: jfvGxFi42rocDzYgqMeACLMjomiye3NZ6SpK5BMl9TU -pyRFC3339==1.0 - -# sha256: Z9WdZs26jWJOA4m4eyqDoXbyHxaodVO1D1cDsj8pusI -python-augeas==0.5.0 - -# sha256: BOk_JJlcQ92Q8zjV2GXKcs4_taU1jU2qSWVXHbNfw-w -# sha256: Pm9ZP-rZj4pSa8PjBpM1MyNuM3KfVS9SiW6lBPVTE_o -python2-pythondialog==3.3.0 - -# sha256: Or5qbT_C-75MYBRCEfRdou2-MYKm9lEa9ru6BZix-ZI -# sha256: k575weEiTZgEBWial__PeCjFbRUXsx1zRkNWwfK3dp4 -# sha256: 6tSu-nAHJJ4F5RsBCVcZ1ajdlXYAifVzCqxWmLGTKRg -# sha256: PMoN8IvQ7ZhDI5BJTOPe0AP15mGqRgvnpzS__jWYNgU -# sha256: Pt5HDT0XujwHY436DRBFK8G25a0yYSemW6d-aq6xG-w -# sha256: aMR5ZPcYbuwwaxNilidyK5B5zURH7Z5eyuzU6shMpzQ -# sha256: 3V05kZUKrkCmyB3hV4lC5z1imAjO_FHRLNFXmA5s_Bg -# sha256: p3xSBiwH63x7MFRdvHPjKZW34Rfup1Axe1y1x6RhjxQ -# sha256: ga-a7EvJYKmgEnxIjxh3La5GNGiSM_BvZUQ-exHr61E -# sha256: 4Hmx2txcBiRswbtv4bI6ULHRFz8u3VEE79QLtzoo9AY -# sha256: -9JnRncsJMuTyLl8va1cueRshrvbG52KdD7gDi-x_F0 -# sha256: mSZu8wo35Dky3uwrfKc-g8jbw7n_cD7HPsprHa5r7-o -# sha256: i2zhyZOQl4O8luC0806iI7_3pN8skL25xODxrJKGieM -pytz==2015.7 - -# sha256: ET-7pVManjSUW302szoIToul0GZLcDyBp8Vy2RkZpbg -# sha256: xXeBXdAPE5QgP8ROuXlySwmPiCZKnviY7kW45enPWH8 -requests==2.9.1 - -# sha256: D_eMQD2bzPWkJabTGhKqa0fxwhyk3CVzp-LzKpczXrE -# sha256: EF-NaGFvgkjiS_DpNy7wTTzBAQTxmA9U1Xss5zpa1Wo -six==1.10.0 - -# sha256: glPOvsSxkJTWfMXtWvmb8duhKFKSIm6Yoxkp-HpdayM -# sha256: BazGegmYDC7P7dNCP3rgEEg57MtV_GRXc-HKoJUcMDA -traceback2==1.4.0 - -# sha256: E_d9CHXbbZtDXh1PQedK1MwutuHVyCSZYJKzQw8Ii7g -# sha256: IogqDkGMKE4fcYqCKzsCKUTVPS2QjhaQsxmp0-ssBXk -unittest2==1.1.0 - -# sha256: KCwRK1XdjjyGmjVx-GdnwVCrEoSprOK97CJsWSrK-Bo -zope.component==4.2.2 - -# sha256: 3HpZov2Rcw03kxMaXSYbKek-xOKpfxvEh86N7-4v54Y -zope.event==4.1.0 - -# sha256: 8HtjH3pgHNjL0zMtVPQxQscIioMpn4WTVvCNHU1CWbM -# sha256: 3lzKCDuUOdgAL7drvmtJmMWlpyH6sluEKYln8ALfTJQ -# sha256: Z4hBb36n9bipe-lIJTd6ol6L3HNGPge6r5hYsp5zcHc -# sha256: bzIw9yVFGCAeWjcIy7LemMhIME8G497Yv7OeWCXLouE -# sha256: X6V1pSQPBCAMMIhCfQ1Le3N_bpAYgYpR2ND5J6aiUXo -# sha256: UiGUrWpUVzXt11yKg_SNZdGvBk5DKn0yDWT1a6_BLpk -# sha256: 6Mey1AlD9xyZFIyX9myqf1E0FH9XQj-NtbSCUJnOmgk -# sha256: J5Ak8CCGAcPKqQfFOHbjetiGJffq8cs4QtvjYLIocBc -# sha256: LiIanux8zFiImieOoT3P7V75OdgLB4Gamos8scaBSE8 -# sha256: aRGJZUEOyG1E3GuQF-4929WC4MCr7vYrOhnb9sitEys -# sha256: 0E34aG7IZNDK3ozxmff4OuzUFhCaIINNVo-DEN7RLeo -# sha256: 51qUfhXul-fnHgLqMC_rL8YtOiu0Zov5377UOlBqx-c -# sha256: TkXSL7iDIipaufKCoRb-xe4ujRpWjM_2otdbvQ62vPw -# sha256: vOkzm7PHpV4IA7Y9IcWDno5Hm8hcSt9CrkFbcvlPrLI -# sha256: koE4NlJFoOiGmlmZ-8wqRUdaCm7VKklNYNvcVAM1_t0 -# sha256: DYQbobuEDuoOZIncXsr6YSVVSXH1O1rLh3ZEQeYbzro -# sha256: sJyMHUezUxxADgGVaX8UFKYyId5u9HhZik8UYPfZo5I -zope.interface==4.1.3 - -# sha256: uDndLZwRfHAUMMFJlWkYpCOphjtIsJyQ4wpgE-fS9E8 -# sha256: j4MIDaoknQNsvM-4rlzG_wB7iNbZN1ITca-r57Gbrbw -mock==1.0.1 - -# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; -# ADD ALL DEPENDENCIES ABOVE - -# sha256: UMVihR1TbyvQNHzx1CzYiydDitJVGw_mLAGr3-gCGJk -# sha256: ClkIqiGQsLTyyLASRkWYniS9n4CAW6D4GSuBETXFALY -acme==0.4.2 - -# sha256: hbUGND6Eo_q6a97o3o66wwLYJ7koNvwOXh9u5bZNCVI -# sha256: 460kqywseljbDW_Gr_ZU23rWlzNeE-AL4_JwYCRdS-Y -letsencrypt==0.4.2 - -# sha256: KNMAOMrJMr1vLJBDaihGqEmvPbfxgH_dvRk1OFHaM_I -# sha256: SXSg-gIabiV4CBzrfPIyABhfTjKl7YZrKDSVkfE4Vbo -letsencrypt-apache==0.4.2 +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 UNLIKELY_EOF # ------------------------------------------------------------------------- - cat << "UNLIKELY_EOF" > "$TEMP_DIR/peep.py" + cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" #!/usr/bin/env python -"""peep ("prudently examine every package") verifies that packages conform to a -trusted, locally stored hash and only then installs them:: +"""A small script that can act as a trust root for installing pip 8 - peep install -r requirements.txt - -This makes your deployments verifiably repeatable without having to maintain a -local PyPI mirror or use a vendor lib. Just update the version numbers and -hashes in requirements.txt, and you're all set. +Embed this in your project, and your VCS checkout is all you have to trust. In +a post-peep era, this lets you claw your way to a hash-checking version of pip, +with which you can install the rest of your dependencies safely. All it assumes +is Python 2.6 or better and *some* version of pip already installed. If +anything goes wrong, it will exit with a non-zero status code. """ -# This is here so embedded copies of peep.py are MIT-compliant: -# Copyright (c) 2013 Erik Rose +# This is here so embedded copies are MIT-compliant: +# Copyright (c) 2016 Erik Rose # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -717,957 +681,146 @@ hashes in requirements.txt, and you're all set. # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. from __future__ import print_function -try: - xrange = xrange -except NameError: - xrange = range -from base64 import urlsafe_b64encode, urlsafe_b64decode -from binascii import hexlify -import cgi -from collections import defaultdict -from functools import wraps from hashlib import sha256 -from itertools import chain, islice -import mimetypes -from optparse import OptionParser -from os.path import join, basename, splitext, isdir -from pickle import dumps, loads -import re -import sys -from shutil import rmtree, copy -from sys import argv, exit -from tempfile import mkdtemp -import traceback +from os.path import join +from pipes import quote +from shutil import rmtree try: - from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError + from subprocess import check_output +except ImportError: + from subprocess import CalledProcessError, PIPE, Popen + + def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be ' + 'overridden.') + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return output +from sys import exit, version_info +from tempfile import mkdtemp +try: + from urllib2 import build_opener, HTTPHandler, HTTPSHandler except ImportError: from urllib.request import build_opener, HTTPHandler, HTTPSHandler - from urllib.error import HTTPError try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse # 3.4 -# TODO: Probably use six to make urllib stuff work across 2/3. - -from pkg_resources import require, VersionConflict, DistributionNotFound - -# We don't admit our dependency on pip in setup.py, lest a naive user simply -# say `pip install peep.tar.gz` and thus pull down an untrusted copy of pip -# from PyPI. Instead, we make sure it's installed and new enough here and spit -# out an error message if not: -def activate(specifier): - """Make a compatible version of pip importable. Raise a RuntimeError if we - couldn't.""" - try: - for distro in require(specifier): - distro.activate() - except (VersionConflict, DistributionNotFound): - raise RuntimeError('The installed version of pip is too old; peep ' - 'requires ' + specifier) - -# Before 0.6.2, the log module wasn't there, so some -# of our monkeypatching fails. It probably wouldn't be -# much work to support even earlier, though. -activate('pip>=0.6.2') - -import pip -from pip.commands.install import InstallCommand -try: - from pip.download import url_to_path # 1.5.6 -except ImportError: - try: - from pip.util import url_to_path # 0.7.0 - except ImportError: - from pip.util import url_to_filename as url_to_path # 0.6.2 -from pip.exceptions import InstallationError -from pip.index import PackageFinder, Link -try: - from pip.log import logger -except ImportError: - from pip import logger # 6.0 -from pip.req import parse_requirements -try: - from pip.utils.ui import DownloadProgressBar, DownloadProgressSpinner -except ImportError: - class NullProgressBar(object): - def __init__(self, *args, **kwargs): - pass - - def iter(self, ret, *args, **kwargs): - return ret - - DownloadProgressBar = DownloadProgressSpinner = NullProgressBar - -__version__ = 3, 1, 1 - -try: - from pip.index import FormatControl # noqa - FORMAT_CONTROL_ARG = 'format_control' - - # The line-numbering bug will be fixed in pip 8. All 7.x releases had it. - PIP_MAJOR_VERSION = int(pip.__version__.split('.')[0]) - PIP_COUNTS_COMMENTS = PIP_MAJOR_VERSION >= 8 -except ImportError: - FORMAT_CONTROL_ARG = 'use_wheel' # pre-7 - PIP_COUNTS_COMMENTS = True +__version__ = 1, 1, 1 -ITS_FINE_ITS_FINE = 0 -SOMETHING_WENT_WRONG = 1 -# "Traditional" for command-line errors according to optparse docs: -COMMAND_LINE_ERROR = 2 -UNHANDLED_EXCEPTION = 3 - -ARCHIVE_EXTENSIONS = ('.tar.bz2', '.tar.gz', '.tgz', '.tar', '.zip') - -MARKER = object() +# wheel has a conditional dependency on argparse: +maybe_argparse = ( + [('https://pypi.python.org/packages/source/a/argparse/' + 'argparse-1.4.0.tar.gz', + '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] + if version_info < (2, 7, 0) else []) -class PipException(Exception): - """When I delegated to pip, it exited with an error.""" - - def __init__(self, error_code): - self.error_code = error_code +PACKAGES = maybe_argparse + [ + # Pip has no dependencies, as it vendors everything: + ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', + '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + # This version of setuptools has only optional dependencies: + ('https://pypi.python.org/packages/source/s/setuptools/' + 'setuptools-20.2.2.tar.gz', + '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), + ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') +] -class UnsupportedRequirementError(Exception): - """An unsupported line was encountered in a requirements file.""" - - -class DownloadError(Exception): - def __init__(self, link, exc): - self.link = link - self.reason = str(exc) - +class HashError(Exception): def __str__(self): - return 'Downloading %s failed: %s' % (self.link, self.reason) + url, path, actual, expected = self.args + return ('{url} did not match the expected hash {expected}. Instead, ' + 'it was {actual}. The file (left at {path}) may have been ' + 'tampered with.'.format(**locals())) -def encoded_hash(sha): - """Return a short, 7-bit-safe representation of a hash. +def hashed_download(url, temp, digest): + """Download ``url`` to ``temp``, make sure it has the SHA-256 ``digest``, + and return its path.""" + # Based on pip 1.4.1's URLOpener but with cert verification removed. Python + # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert + # authenticity has only privacy (not arbitrary code execution) + # implications, since we're checking hashes. + def opener(): + opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) + return opener - If you pass a sha256, this results in the hash algorithm that the Wheel - format (PEP 427) uses, except here it's intended to be run across the - downloaded archive before unpacking. - - """ - return urlsafe_b64encode(sha.digest()).decode('ascii').rstrip('=') - - -def path_and_line(req): - """Return the path and line number of the file from which an - InstallRequirement came. - - """ - path, line = (re.match(r'-r (.*) \(line (\d+)\)$', - req.comes_from).groups()) - return path, int(line) - - -def hashes_above(path, line_number): - """Yield hashes from contiguous comment lines before line ``line_number``. - - """ - def hash_lists(path): - """Yield lists of hashes appearing between non-comment lines. - - The lists will be in order of appearance and, for each non-empty - list, their place in the results will coincide with that of the - line number of the corresponding result from `parse_requirements` - (which changed in pip 7.0 to not count comments). - - """ - hashes = [] - with open(path) as file: - for lineno, line in enumerate(file, 1): - match = HASH_COMMENT_RE.match(line) - if match: # Accumulate this hash. - hashes.append(match.groupdict()['hash']) - if not IGNORED_LINE_RE.match(line): - yield hashes # Report hashes seen so far. - hashes = [] - elif PIP_COUNTS_COMMENTS: - # Comment: count as normal req but have no hashes. - yield [] - - return next(islice(hash_lists(path), line_number - 1, None)) - - -def run_pip(initial_args): - """Delegate to pip the given args (starting with the subcommand), and raise - ``PipException`` if something goes wrong.""" - status_code = pip.main(initial_args) - - # Clear out the registrations in the pip "logger" singleton. Otherwise, - # loggers keep getting appended to it with every run. Pip assumes only one - # command invocation will happen per interpreter lifetime. - logger.consumers = [] - - if status_code: - raise PipException(status_code) - - -def hash_of_file(path): - """Return the hash of a downloaded file.""" - with open(path, 'rb') as archive: - sha = sha256() + def read_chunks(response, chunk_size): while True: - data = archive.read(2 ** 20) - if not data: + chunk = response.read(chunk_size) + if not chunk: break - sha.update(data) - return encoded_hash(sha) - - -def is_git_sha(text): - """Return whether this is probably a git sha""" - # Handle both the full sha as well as the 7-character abbreviation - if len(text) in (40, 7): - try: - int(text, 16) - return True - except ValueError: - pass - return False - - -def filename_from_url(url): - parsed = urlparse(url) - path = parsed.path - return path.split('/')[-1] - - -def requirement_args(argv, want_paths=False, want_other=False): - """Return an iterable of filtered arguments. - - :arg argv: Arguments, starting after the subcommand - :arg want_paths: If True, the returned iterable includes the paths to any - requirements files following a ``-r`` or ``--requirement`` option. - :arg want_other: If True, the returned iterable includes the args that are - not a requirement-file path or a ``-r`` or ``--requirement`` flag. - - """ - was_r = False - for arg in argv: - # Allow for requirements files named "-r", don't freak out if there's a - # trailing "-r", etc. - if was_r: - if want_paths: - yield arg - was_r = False - elif arg in ['-r', '--requirement']: - was_r = True - else: - if want_other: - yield arg - -# any line that is a comment or just whitespace -IGNORED_LINE_RE = re.compile(r'^(\s*#.*)?\s*$') - -HASH_COMMENT_RE = re.compile( - r""" - \s*\#\s+ # Lines that start with a '#' - (?Psha256):\s+ # Hash type is hardcoded to be sha256 for now. - (?P[^\s]+) # Hashes can be anything except '#' or spaces. - \s* # Suck up whitespace before the comment or - # just trailing whitespace if there is no - # comment. Also strip trailing newlines. - (?:\#(?P.*))? # Comments can be anything after a whitespace+# - # and are optional. - $""", re.X) - - -def peep_hash(argv): - """Return the peep hash of one or more files, returning a shell status code - or raising a PipException. - - :arg argv: The commandline args, starting after the subcommand - - """ - parser = OptionParser( - usage='usage: %prog hash file [file ...]', - description='Print a peep hash line for one or more files: for ' - 'example, "# sha256: ' - 'oz42dZy6Gowxw8AelDtO4gRgTW_xPdooH484k7I5EOY".') - _, paths = parser.parse_args(args=argv) - if paths: - for path in paths: - print('# sha256:', hash_of_file(path)) - return ITS_FINE_ITS_FINE - else: - parser.print_usage() - return COMMAND_LINE_ERROR - - -class EmptyOptions(object): - """Fake optparse options for compatibility with pip<1.2 - - pip<1.2 had a bug in parse_requirements() in which the ``options`` kwarg - was required. We work around that by passing it a mock object. - - """ - default_vcs = None - skip_requirements_regex = None - isolated_mode = False - - -def memoize(func): - """Memoize a method that should return the same result every time on a - given instance. - - """ - @wraps(func) - def memoizer(self): - if not hasattr(self, '_cache'): - self._cache = {} - if func.__name__ not in self._cache: - self._cache[func.__name__] = func(self) - return self._cache[func.__name__] - return memoizer - - -def package_finder(argv): - """Return a PackageFinder respecting command-line options. - - :arg argv: Everything after the subcommand - - """ - # We instantiate an InstallCommand and then use some of its private - # machinery--its arg parser--for our own purposes, like a virus. This - # approach is portable across many pip versions, where more fine-grained - # ones are not. Ignoring options that don't exist on the parser (for - # instance, --use-wheel) gives us a straightforward method of backward - # compatibility. - try: - command = InstallCommand() - except TypeError: - # This is likely pip 1.3.0's "__init__() takes exactly 2 arguments (1 - # given)" error. In that version, InstallCommand takes a top=level - # parser passed in from outside. - from pip.baseparser import create_main_parser - command = InstallCommand(create_main_parser()) - # The downside is that it essentially ruins the InstallCommand class for - # further use. Calling out to pip.main() within the same interpreter, for - # example, would result in arguments parsed this time turning up there. - # Thus, we deepcopy the arg parser so we don't trash its singletons. Of - # course, deepcopy doesn't work on these objects, because they contain - # uncopyable regex patterns, so we pickle and unpickle instead. Fun! - options, _ = loads(dumps(command.parser)).parse_args(argv) - - # Carry over PackageFinder kwargs that have [about] the same names as - # options attr names: - possible_options = [ - 'find_links', - FORMAT_CONTROL_ARG, - ('allow_all_prereleases', 'pre'), - 'process_dependency_links' - ] - kwargs = {} - for option in possible_options: - kw, attr = option if isinstance(option, tuple) else (option, option) - value = getattr(options, attr, MARKER) - if value is not MARKER: - kwargs[kw] = value - - # Figure out index_urls: - index_urls = [options.index_url] + options.extra_index_urls - if options.no_index: - index_urls = [] - index_urls += getattr(options, 'mirrors', []) - - # If pip is new enough to have a PipSession, initialize one, since - # PackageFinder requires it: - if hasattr(command, '_build_session'): - kwargs['session'] = command._build_session(options) - - return PackageFinder(index_urls=index_urls, **kwargs) - - -class DownloadedReq(object): - """A wrapper around InstallRequirement which offers additional information - based on downloading and examining a corresponding package archive - - These are conceptually immutable, so we can get away with memoizing - expensive things. - - """ - def __init__(self, req, argv, finder): - """Download a requirement, compare its hashes, and return a subclass - of DownloadedReq depending on its state. - - :arg req: The InstallRequirement I am based on - :arg argv: The args, starting after the subcommand - - """ - self._req = req - self._argv = argv - self._finder = finder - - # We use a separate temp dir for each requirement so requirements - # (from different indices) that happen to have the same archive names - # don't overwrite each other, leading to a security hole in which the - # latter is a hash mismatch, the former has already passed the - # comparison, and the latter gets installed. - self._temp_path = mkdtemp(prefix='peep-') - # Think of DownloadedReq as a one-shot state machine. It's an abstract - # class that ratchets forward to being one of its own subclasses, - # depending on its package status. Then it doesn't move again. - self.__class__ = self._class() - - def dispose(self): - """Delete temp files and dirs I've made. Render myself useless. - - Do not call further methods on me after calling dispose(). - - """ - rmtree(self._temp_path) - - def _version(self): - """Deduce the version number of the downloaded package from its filename.""" - # TODO: Can we delete this method and just print the line from the - # reqs file verbatim instead? - def version_of_archive(filename, package_name): - # Since we know the project_name, we can strip that off the left, strip - # any archive extensions off the right, and take the rest as the - # version. - for ext in ARCHIVE_EXTENSIONS: - if filename.endswith(ext): - filename = filename[:-len(ext)] - break - # Handle github sha tarball downloads. - if is_git_sha(filename): - filename = package_name + '-' + filename - if not filename.lower().replace('_', '-').startswith(package_name.lower()): - # TODO: Should we replace runs of [^a-zA-Z0-9.], not just _, with -? - give_up(filename, package_name) - return filename[len(package_name) + 1:] # Strip off '-' before version. - - def version_of_wheel(filename, package_name): - # For Wheel files (http://legacy.python.org/dev/peps/pep-0427/#file- - # name-convention) we know the format bits are '-' separated. - whl_package_name, version, _rest = filename.split('-', 2) - # Do the alteration to package_name from PEP 427: - our_package_name = re.sub(r'[^\w\d.]+', '_', package_name, re.UNICODE) - if whl_package_name != our_package_name: - give_up(filename, whl_package_name) - return version - - def give_up(filename, package_name): - raise RuntimeError("The archive '%s' didn't start with the package name " - "'%s', so I couldn't figure out the version number. " - "My bad; improve me." % - (filename, package_name)) - - get_version = (version_of_wheel - if self._downloaded_filename().endswith('.whl') - else version_of_archive) - return get_version(self._downloaded_filename(), self._project_name()) - - def _is_always_unsatisfied(self): - """Returns whether this requirement is always unsatisfied - - This would happen in cases where we can't determine the version - from the filename. - - """ - # If this is a github sha tarball, then it is always unsatisfied - # because the url has a commit sha in it and not the version - # number. - url = self._url() - if url: - filename = filename_from_url(url) - if filename.endswith(ARCHIVE_EXTENSIONS): - filename, ext = splitext(filename) - if is_git_sha(filename): - return True - return False - - @memoize # Avoid hitting the file[cache] over and over. - def _expected_hashes(self): - """Return a list of known-good hashes for this package.""" - return hashes_above(*path_and_line(self._req)) - - def _download(self, link): - """Download a file, and return its name within my temp dir. - - This does no verification of HTTPS certs, but our checking hashes - makes that largely unimportant. It would be nice to be able to use the - requests lib, which can verify certs, but it is guaranteed to be - available only in pip >= 1.5. - - This also drops support for proxies and basic auth, though those could - be added back in. - - """ - # Based on pip 1.4.1's URLOpener but with cert verification removed - def opener(is_https): - if is_https: - opener = build_opener(HTTPSHandler()) - # Strip out HTTPHandler to prevent MITM spoof: - for handler in opener.handlers: - if isinstance(handler, HTTPHandler): - opener.handlers.remove(handler) - else: - opener = build_opener() - return opener - - # Descended from unpack_http_url() in pip 1.4.1 - def best_filename(link, response): - """Return the most informative possible filename for a download, - ideally with a proper extension. - - """ - content_type = response.info().get('content-type', '') - filename = link.filename # fallback - # Have a look at the Content-Disposition header for a better guess: - content_disposition = response.info().get('content-disposition') - if content_disposition: - type, params = cgi.parse_header(content_disposition) - # We use ``or`` here because we don't want to use an "empty" value - # from the filename param: - filename = params.get('filename') or filename - ext = splitext(filename)[1] - if not ext: - ext = mimetypes.guess_extension(content_type) - if ext: - filename += ext - if not ext and link.url != response.geturl(): - ext = splitext(response.geturl())[1] - if ext: - filename += ext - return filename - - # Descended from _download_url() in pip 1.4.1 - def pipe_to_file(response, path, size=0): - """Pull the data off an HTTP response, shove it in a new file, and - show progress. - - :arg response: A file-like object to read from - :arg path: The path of the new file - :arg size: The expected size, in bytes, of the download. 0 for - unknown or to suppress progress indication (as for cached - downloads) - - """ - def response_chunks(chunk_size): - while True: - chunk = response.read(chunk_size) - if not chunk: - break - yield chunk - - print('Downloading %s%s...' % ( - self._req.req, - (' (%sK)' % (size / 1000)) if size > 1000 else '')) - progress_indicator = (DownloadProgressBar(max=size).iter if size - else DownloadProgressSpinner().iter) - with open(path, 'wb') as file: - for chunk in progress_indicator(response_chunks(4096), 4096): - file.write(chunk) - - url = link.url.split('#', 1)[0] - try: - response = opener(urlparse(url).scheme != 'http').open(url) - except (HTTPError, IOError) as exc: - raise DownloadError(link, exc) - filename = best_filename(link, response) - try: - size = int(response.headers['content-length']) - except (ValueError, KeyError, TypeError): - size = 0 - pipe_to_file(response, join(self._temp_path, filename), size=size) - return filename - - # Based on req_set.prepare_files() in pip bb2a8428d4aebc8d313d05d590f386fa3f0bbd0f - @memoize # Avoid re-downloading. - def _downloaded_filename(self): - """Download the package's archive if necessary, and return its - filename. - - --no-deps is implied, as we have reimplemented the bits that would - ordinarily do dependency resolution. - - """ - # Peep doesn't support requirements that don't come down as a single - # file, because it can't hash them. Thus, it doesn't support editable - # requirements, because pip itself doesn't support editable - # requirements except for "local projects or a VCS url". Nor does it - # support VCS requirements yet, because we haven't yet come up with a - # portable, deterministic way to hash them. In summary, all we support - # is == requirements and tarballs/zips/etc. - - # TODO: Stop on reqs that are editable or aren't ==. - - # If the requirement isn't already specified as a URL, get a URL - # from an index: - link = self._link() or self._finder.find_requirement(self._req, upgrade=False) - - if link: - lower_scheme = link.scheme.lower() # pip lower()s it for some reason. - if lower_scheme == 'http' or lower_scheme == 'https': - file_path = self._download(link) - return basename(file_path) - elif lower_scheme == 'file': - # The following is inspired by pip's unpack_file_url(): - link_path = url_to_path(link.url_without_fragment) - if isdir(link_path): - raise UnsupportedRequirementError( - "%s: %s is a directory. So that it can compute " - "a hash, peep supports only filesystem paths which " - "point to files" % - (self._req, link.url_without_fragment)) - else: - copy(link_path, self._temp_path) - return basename(link_path) - else: - raise UnsupportedRequirementError( - "%s: The download link, %s, would not result in a file " - "that can be hashed. Peep supports only == requirements, " - "file:// URLs pointing to files (not folders), and " - "http:// and https:// URLs pointing to tarballs, zips, " - "etc." % (self._req, link.url)) - else: - raise UnsupportedRequirementError( - "%s: couldn't determine where to download this requirement from." - % (self._req,)) - - def install(self): - """Install the package I represent, without dependencies. - - Obey typical pip-install options passed in on the command line. - - """ - other_args = list(requirement_args(self._argv, want_other=True)) - archive_path = join(self._temp_path, self._downloaded_filename()) - # -U so it installs whether pip deems the requirement "satisfied" or - # not. This is necessary for GitHub-sourced zips, which change without - # their version numbers changing. - run_pip(['install'] + other_args + ['--no-deps', '-U', archive_path]) - - @memoize - def _actual_hash(self): - """Download the package's archive if necessary, and return its hash.""" - return hash_of_file(join(self._temp_path, self._downloaded_filename())) - - def _project_name(self): - """Return the inner Requirement's "unsafe name". - - Raise ValueError if there is no name. - - """ - name = getattr(self._req.req, 'project_name', '') - if name: - return name - raise ValueError('Requirement has no project_name.') - - def _name(self): - return self._req.name - - def _link(self): - try: - return self._req.link - except AttributeError: - # The link attribute isn't available prior to pip 6.1.0, so fall - # back to the now deprecated 'url' attribute. - return Link(self._req.url) if self._req.url else None - - def _url(self): - link = self._link() - return link.url if link else None - - @memoize # Avoid re-running expensive check_if_exists(). - def _is_satisfied(self): - self._req.check_if_exists() - return (self._req.satisfied_by and - not self._is_always_unsatisfied()) - - def _class(self): - """Return the class I should be, spanning a continuum of goodness.""" - try: - self._project_name() - except ValueError: - return MalformedReq - if self._is_satisfied(): - return SatisfiedReq - if not self._expected_hashes(): - return MissingReq - if self._actual_hash() not in self._expected_hashes(): - return MismatchedReq - return InstallableReq - - @classmethod - def foot(cls): - """Return the text to be printed once, after all of the errors from - classes of my type are printed. - - """ - return '' - - -class MalformedReq(DownloadedReq): - """A requirement whose package name could not be determined""" - - @classmethod - def head(cls): - return 'The following requirements could not be processed:\n' - - def error(self): - return '* Unable to determine package name from URL %s; add #egg=' % self._url() - - -class MissingReq(DownloadedReq): - """A requirement for which no hashes were specified in the requirements file""" - - @classmethod - def head(cls): - return ('The following packages had no hashes specified in the requirements file, which\n' - 'leaves them open to tampering. Vet these packages to your satisfaction, then\n' - 'add these "sha256" lines like so:\n\n') - - def error(self): - if self._url(): - # _url() always contains an #egg= part, or this would be a - # MalformedRequest. - line = self._url() - else: - line = '%s==%s' % (self._name(), self._version()) - return '# sha256: %s\n%s\n' % (self._actual_hash(), line) - - -class MismatchedReq(DownloadedReq): - """A requirement for which the downloaded file didn't match any of my hashes.""" - @classmethod - def head(cls): - return ("THE FOLLOWING PACKAGES DIDN'T MATCH THE HASHES SPECIFIED IN THE REQUIREMENTS\n" - "FILE. If you have updated the package versions, update the hashes. If not,\n" - "freak out, because someone has tampered with the packages.\n\n") - - def error(self): - preamble = ' %s: expected' % self._project_name() - if len(self._expected_hashes()) > 1: - preamble += ' one of' - padding = '\n' + ' ' * (len(preamble) + 1) - return '%s %s\n%s got %s' % (preamble, - padding.join(self._expected_hashes()), - ' ' * (len(preamble) - 4), - self._actual_hash()) - - @classmethod - def foot(cls): - return '\n' - - -class SatisfiedReq(DownloadedReq): - """A requirement which turned out to be already installed""" - - @classmethod - def head(cls): - return ("These packages were already installed, so we didn't need to download or build\n" - "them again. If you installed them with peep in the first place, you should be\n" - "safe. If not, uninstall them, then re-attempt your install with peep.\n") - - def error(self): - return ' %s' % (self._req,) - - -class InstallableReq(DownloadedReq): - """A requirement whose hash matched and can be safely installed""" - - -# DownloadedReq subclasses that indicate an error that should keep us from -# going forward with installation, in the order in which their errors should -# be reported: -ERROR_CLASSES = [MismatchedReq, MissingReq, MalformedReq] - - -def bucket(things, key): - """Return a map of key -> list of things.""" - ret = defaultdict(list) - for thing in things: - ret[key(thing)].append(thing) - return ret - - -def first_every_last(iterable, first, every, last): - """Execute something before the first item of iter, something else for each - item, and a third thing after the last. - - If there are no items in the iterable, don't execute anything. - - """ - did_first = False - for item in iterable: - if not did_first: - did_first = True - first(item) - every(item) - if did_first: - last(item) - - -def _parse_requirements(path, finder): - try: - # list() so the generator that is parse_requirements() actually runs - # far enough to report a TypeError - return list(parse_requirements( - path, options=EmptyOptions(), finder=finder)) - except TypeError: - # session is a required kwarg as of pip 6.0 and will raise - # a TypeError if missing. It needs to be a PipSession instance, - # but in older versions we can't import it from pip.download - # (nor do we need it at all) so we only import it in this except block - from pip.download import PipSession - return list(parse_requirements( - path, options=EmptyOptions(), session=PipSession(), finder=finder)) - - -def downloaded_reqs_from_path(path, argv): - """Return a list of DownloadedReqs representing the requirements parsed - out of a given requirements file. - - :arg path: The path to the requirements file - :arg argv: The commandline args, starting after the subcommand - - """ - finder = package_finder(argv) - return [DownloadedReq(req, argv, finder) for req in - _parse_requirements(path, finder)] - - -def peep_install(argv): - """Perform the ``peep install`` subcommand, returning a shell status code - or raising a PipException. - - :arg argv: The commandline args, starting after the subcommand - - """ - output = [] - out = output.append - reqs = [] - try: - req_paths = list(requirement_args(argv, want_paths=True)) - if not req_paths: - out("You have to specify one or more requirements files with the -r option, because\n" - "otherwise there's nowhere for peep to look up the hashes.\n") - return COMMAND_LINE_ERROR - - # We're a "peep install" command, and we have some requirement paths. - reqs = list(chain.from_iterable( - downloaded_reqs_from_path(path, argv) - for path in req_paths)) - buckets = bucket(reqs, lambda r: r.__class__) - - # Skip a line after pip's "Cleaning up..." so the important stuff - # stands out: - if any(buckets[b] for b in ERROR_CLASSES): - out('\n') - - printers = (lambda r: out(r.head()), - lambda r: out(r.error() + '\n'), - lambda r: out(r.foot())) - for c in ERROR_CLASSES: - first_every_last(buckets[c], *printers) - - if any(buckets[b] for b in ERROR_CLASSES): - out('-------------------------------\n' - 'Not proceeding to installation.\n') - return SOMETHING_WENT_WRONG - else: - for req in buckets[InstallableReq]: - req.install() - - first_every_last(buckets[SatisfiedReq], *printers) - - return ITS_FINE_ITS_FINE - except (UnsupportedRequirementError, InstallationError, DownloadError) as exc: - out(str(exc)) - return SOMETHING_WENT_WRONG - finally: - for req in reqs: - req.dispose() - print(''.join(output)) - - -def peep_port(paths): - """Convert a peep requirements file to one compatble with pip-8 hashing. - - Loses comments and tromps on URLs, so the result will need a little manual - massaging, but the hard part--the hash conversion--is done for you. - - """ - if not paths: - print('Please specify one or more requirements files so I have ' - 'something to port.\n') - return COMMAND_LINE_ERROR - - comes_from = None - for req in chain.from_iterable( - _parse_requirements(path, package_finder(argv)) for path in paths): - req_path, req_line = path_and_line(req) - hashes = [hexlify(urlsafe_b64decode((hash + '=').encode('ascii'))).decode('ascii') - for hash in hashes_above(req_path, req_line)] - if req_path != comes_from: - print() - print('# from %s' % req_path) - print() - comes_from = req_path - - if not hashes: - print(req.req) - else: - print('%s' % (req.link if getattr(req, 'link', None) else req.req), end='') - for hash in hashes: - print(' \\') - print(' --hash=sha256:%s' % hash, end='') - print() + yield chunk + + response = opener().open(url) + path = join(temp, urlparse(url).path.split('/')[-1]) + actual_hash = sha256() + with open(path, 'wb') as file: + for chunk in read_chunks(response, 4096): + file.write(chunk) + actual_hash.update(chunk) + + actual_digest = actual_hash.hexdigest() + if actual_digest != digest: + raise HashError(url, path, actual_digest, digest) + return path def main(): - """Be the top-level entrypoint. Return a shell status code.""" - commands = {'hash': peep_hash, - 'install': peep_install, - 'port': peep_port} + temp = mkdtemp(prefix='pipstrap-') try: - if len(argv) >= 2 and argv[1] in commands: - return commands[argv[1]](argv[2:]) - else: - # Fall through to top-level pip main() for everything else: - return pip.main() - except PipException as exc: - return exc.error_code - - -def exception_handler(exc_type, exc_value, exc_tb): - print('Oh no! Peep had a problem while trying to do stuff. Please write up a bug report') - print('with the specifics so we can fix it:') - print() - print('https://github.com/erikrose/peep/issues/new') - print() - print('Here are some particulars you can copy and paste into the bug report:') - print() - print('---') - print('peep:', repr(__version__)) - print('python:', repr(sys.version)) - print('pip:', repr(getattr(pip, '__version__', 'no __version__ attr'))) - print('Command line: ', repr(sys.argv)) - print( - ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))) - print('---') + downloads = [hashed_download(url, temp, digest) + for url, digest in PACKAGES] + check_output('pip install --no-index --no-deps -U ' + + ' '.join(quote(d) for d in downloads), + shell=True) + except HashError as exc: + print(exc) + except Exception: + rmtree(temp) + raise + else: + rmtree(temp) + return 0 + return 1 if __name__ == '__main__': - try: - exit(main()) - except Exception: - exception_handler(*sys.exc_info()) - exit(UNHANDLED_EXCEPTION) + exit(main()) UNLIKELY_EOF # ------------------------------------------------------------------------- - InstallRequirements "setuptools-requirements.txt" - InstallRequirements "letsencrypt-auto-requirements.txt" + # Set PATH so pipstrap upgrades the right (v)env: + PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" + set +e + PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_STATUS=$? + set -e + rm -rf "$TEMP_DIR" + if [ "$PIP_STATUS" != 0 ]; then + # Report error. (Otherwise, be quiet.) + echo "Had a problem while installing Python packages:" + echo "$PIP_OUT" + rm -rf "$VENV_PATH" + exit 1 + fi echo "Installation succeeded." fi echo "Requesting root privileges to run letsencrypt..." diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 98e6d8e65..942fd8ea2 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.5.0.dev0" +LE_AUTO_VERSION="0.5.0" # This script takes the same arguments as the main letsencrypt program, but it # additionally responds to --verbose (more output) and --debug (allow support @@ -645,15 +645,15 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.4.2 \ - --hash=sha256:50c562851d536f2bd0347cf1d42cd88b27438ad2551b0fe62c01abdfe8021899 \ - --hash=sha256:0a5908aa2190b0b4f2c8b0124645989e24bd9f80805ba0f8192b811135c500b6 -letsencrypt==0.4.2 \ - --hash=sha256:85b506343e84a3faba6bdee8de8ebac302d827b92836fc0e5e1f6ee5b64d0952 \ - --hash=sha256:e3ad24ab2c2c7a58db0d6fc6aff654db7ad697335e13e00be3f27060245d4be6 -letsencrypt-apache==0.4.2 \ - --hash=sha256:4974a0fa021a6e2578081ceb7cf23200185f4e32a5ed866b28349591f13855ba \ - --hash=sha256:28d30038cac932bd6f2c90436a2846a849af3db7f1807fddbd19353851da33f2 +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 9e2e610c0f271aba2ec8620ea75a664ec71a0b0a..36ab206aa47ee8d6348340e2e17e66ef840f4832 100644 GIT binary patch literal 256 zcmV+b0ssCNAlb`yp8ViHw_s6WQdeQ~rmeR1#f`~|;Lcvp1i>?dOn)2S?|UXU3-X4k zaZ%*t!KjDe7yUlErpaEi6!y=8HI`DRz)EuS7bCS?0nWEvmfo;6rg zd*P;)T`MMf6qh)Nq~anD`EcT+-{;e}X{!NGWzN5f-uFVW&D40h!-VgVXfn*mr{?;Y4vJd_;=@v>g=fCE0&HR8N!p!aAH z8#Erxi1sEj2Zdym%<&YjXJ9%;%Y|e9?#~icJ0k#V&W79xyG>~1A}+~ literal 256 zcmV+b0ssC|uy?g$nftRtV>s7pxDm(6;}CSIS7w5~>UE92k3&>XqcT@k6Ebm^Do;+X zTNp`XJD1D1X2a@bAvvSYJ_mS@>C)`@RYg4jW;8EELMXG}@z?1)U26jc5aF__pN9&P zzz#t=n}Zy`MTP5br^bN9G|5@||KXwljWE@0V9 z_jE$JFZ`7w^>|gt_7zhIl_g=NrPwbYrxD!?nV|ltx+ce9Lv>VU>_|42M*$cAbwRY+ z@4BQV3axV@vvbKU4y^r~3oLDfg)xSEKkvGt4QybGWKo2-@>atSJ;vBd#+*cbN;8Md GKC2Ds1$@5% diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index 1e76417b7..bc4a0bebe 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -178,12 +178,12 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.4.2 \ - --hash=sha256:50c562851d536f2bd0347cf1d42cd88b27438ad2551b0fe62c01abdfe8021899 \ - --hash=sha256:0a5908aa2190b0b4f2c8b0124645989e24bd9f80805ba0f8192b811135c500b6 -letsencrypt==0.4.2 \ - --hash=sha256:85b506343e84a3faba6bdee8de8ebac302d827b92836fc0e5e1f6ee5b64d0952 \ - --hash=sha256:e3ad24ab2c2c7a58db0d6fc6aff654db7ad697335e13e00be3f27060245d4be6 -letsencrypt-apache==0.4.2 \ - --hash=sha256:4974a0fa021a6e2578081ceb7cf23200185f4e32a5ed866b28349591f13855ba \ - --hash=sha256:28d30038cac932bd6f2c90436a2846a849af3db7f1807fddbd19353851da33f2 +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 67262ba72..56dd926cd 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.5.0' install_requires = [ 'letsencrypt=={0}'.format(version), diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index e53bef059..186de748b 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.5.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index 0dbeb1567..8851aeda7 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.5.0.dev0' +__version__ = '0.5.0' diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index fff8dcfc3..a9712acb7 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0.dev0' +version = '0.5.0' install_requires = [ 'setuptools', # pkg_resources From 37817130b01e8481a018ba96856c8e9150e10152 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 5 Apr 2016 17:55:29 -0700 Subject: [PATCH 191/317] Bump version to 0.6.0 --- acme/setup.py | 2 +- letsencrypt-apache/setup.py | 2 +- letsencrypt-auto-source/letsencrypt-auto | 2 +- letsencrypt-compatibility-test/setup.py | 2 +- letsencrypt-nginx/setup.py | 2 +- letsencrypt/__init__.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 4845d7f1e..41c04fd33 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0' +version = '0.6.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 93b0f3296..c2ec7dabd 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0' +version = '0.6.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 942fd8ea2..111f2b272 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.5.0" +LE_AUTO_VERSION="0.6.0.dev0" # This script takes the same arguments as the main letsencrypt program, but it # additionally responds to --verbose (more output) and --debug (allow support diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index 56dd926cd..25104aeef 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0' +version = '0.6.0.dev0' install_requires = [ 'letsencrypt=={0}'.format(version), diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 186de748b..54b69adc3 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0' +version = '0.6.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index 8851aeda7..3f65e6d83 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.5.0' +__version__ = '0.6.0.dev0' diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index a9712acb7..d505858e4 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.5.0' +version = '0.6.0.dev0' install_requires = [ 'setuptools', # pkg_resources From 45ba43662cfd2e5bd57e3afc6c213e3cea66d406 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 5 Apr 2016 19:01:29 -0700 Subject: [PATCH 192/317] fix help typo --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 58250d75e..2dc3de3d3 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -652,7 +652,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): helpful.add( "automation", "-q", "--quiet", dest="quiet", action="store_true", help="Silence all output except errors. Useful for automation via cron." - "Implies --non-interactive.") + " Implies --non-interactive.") helpful.add_group( "testing", description="The following flags are meant for " From e4076633c8ea79abbd797500c5a325df7b63d58c Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Wed, 6 Apr 2016 06:14:31 +0000 Subject: [PATCH 193/317] Add Directory.meta (fixes #2768) --- acme/acme/messages.py | 12 ++++++++++-- acme/acme/messages_test.py | 18 +++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 06b4492d6..24a3b580c 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -123,6 +123,12 @@ class Directory(jose.JSONDeSerializable): _REGISTERED_TYPES = {} + class Meta(jose.JSONObjectWithFields): + """Directory Meta.""" + terms_of_service = jose.Field('terms-of-service', omitempty=True) + website = jose.Field('website', omitempty=True) + caa_identities = jose.Field('caa-identities', omitempty=True) + @classmethod def _canon_key(cls, key): return getattr(key, 'resource_type', key) @@ -137,10 +143,11 @@ class Directory(jose.JSONDeSerializable): def __init__(self, jobj): canon_jobj = util.map_keys(jobj, self._canon_key) - if not set(canon_jobj).issubset(self._REGISTERED_TYPES): + if not set(canon_jobj).issubset( + set(self._REGISTERED_TYPES).union(['meta'])): # TODO: acme-spec is not clear about this: 'It is a JSON # dictionary, whose keys are the "resource" values listed - # in {{https-requests}}'z + # in {{https-requests}}' raise ValueError('Wrong directory fields') # TODO: check that everything is an absolute URL; acme-spec is # not clear on that @@ -163,6 +170,7 @@ class Directory(jose.JSONDeSerializable): @classmethod def from_json(cls, jobj): + jobj['meta'] = cls.Meta.from_json(jobj.pop('meta', {})) try: return cls(jobj) except ValueError as error: diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index fa558cf4a..b2b7febdc 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -90,6 +90,11 @@ class DirectoryTest(unittest.TestCase): self.dir = Directory({ 'new-reg': 'reg', mock.MagicMock(resource_type='new-cert'): 'cert', + 'meta': Directory.Meta( + terms_of_service='https://example.com/acme/terms', + website='https://www.example.com/', + caa_identities=['example.com'], + ), }) def test_init_wrong_key_value_error(self): @@ -111,9 +116,16 @@ class DirectoryTest(unittest.TestCase): def test_getattr_fails_with_attribute_error(self): self.assertRaises(AttributeError, self.dir.__getattr__, 'foo') - def test_to_partial_json(self): - self.assertEqual( - self.dir.to_partial_json(), {'new-reg': 'reg', 'new-cert': 'cert'}) + def test_to_json(self): + self.assertEqual(self.dir.to_json(), { + 'new-reg': 'reg', + 'new-cert': 'cert', + 'meta': { + 'terms-of-service': 'https://example.com/acme/terms', + 'website': 'https://www.example.com/', + 'caa-identities': ['example.com'], + }, + }) def test_from_json_deserialization_error_on_wrong_key(self): from acme.messages import Directory From c4974e6d937eec5dd85b8fd99c6f477594cc6379 Mon Sep 17 00:00:00 2001 From: Pavel Pavlov Date: Thu, 7 Apr 2016 18:11:55 +0300 Subject: [PATCH 194/317] Nginx map statement hotfix --- letsencrypt-nginx/letsencrypt_nginx/nginxparser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-nginx/letsencrypt_nginx/nginxparser.py b/letsencrypt-nginx/letsencrypt_nginx/nginxparser.py index cef0756d7..69594efc3 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/nginxparser.py +++ b/letsencrypt-nginx/letsencrypt_nginx/nginxparser.py @@ -30,10 +30,11 @@ class RawNginxParser(object): assignment = (key + Optional(space + value, default=None) + semicolon) location_statement = Optional(space + modifier) + Optional(space + location) if_statement = Literal("if") + space + Regex(r"\(.+\)") + space + map_statement = Literal("map") + space + Regex(r"\S+") + space + Regex(r"\$\S+") + space block = Forward() block << Group( - (Group(key + location_statement) ^ Group(if_statement)) + + (Group(key + location_statement) ^ Group(if_statement) ^ Group(map_statement)) + left_bracket + Group(ZeroOrMore(Group(comment | assignment) | block)) + right_bracket) From 3ca825592efd55e1667ba4b2e2f19afb7f89cbbd Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 8 Apr 2016 15:30:27 -0700 Subject: [PATCH 195/317] Reverter.py: clock change protection, and debugging for #1243 --- letsencrypt/reverter.py | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index ea54a91ee..c0771d577 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -4,6 +4,7 @@ import logging import os import shutil import time +import traceback import zope.component @@ -333,16 +334,14 @@ class Reverter(object): # Make sure some files are provided... as this is an error # Made this mistake in my initial implementation of apache.dvsni.py if not files: - raise errors.ReverterError( - "Forgot to provide files to registration call") + raise errors.ReverterError("Forgot to provide files to registration call") cp_dir = self._get_cp_dir(temporary) # Append all new files (that aren't already registered) new_fd = None try: - new_fd, ex_files = self._read_and_append( - os.path.join(cp_dir, "NEW_FILES")) + new_fd, ex_files = self._read_and_append(os.path.join(cp_dir, "NEW_FILES")) for path in files: if path not in ex_files: @@ -503,26 +502,44 @@ class Reverter(object): shutil.move(changes_since_tmp_path, changes_since_path) except (IOError, OSError): logger.error("Unable to finalize checkpoint - adding title") + logger.debug("Exception was:\%s", traceback.format_exc()) raise errors.ReverterError("Unable to add title") self._timestamp_progress_dir() + def _checkpoint_timestamp(self) + "Determine the timestamp of the checkpoint, enforcing monotonicity." + timestamp = str(time.time()) + others = os.listdir(self.config.backup_dir) + others.append(new_dir) + others.sort() + if others[-1] != timestamp: + timetravel = str(float(others[-1]) + 1) + logger.warn("Current timestamp %s does not correspond to newest reverter " + "checkpoint; your clock probably jumped. Time travelling to %s", + new_dir, timetravel) + timestamp = timetravel + elif len(others) > 1 and others[-2] == timestamp: + # It is possible if the checkpoints are made extremely quickly + # that will result in a name collision. + logger.debug("Race condition with timestamp %s, incrementing by 0.01", timestamp) + timetravel = str(float(others[-1]) + 0.01) + timestamp = timetravel + return timestamp + def _timestamp_progress_dir(self): """Timestamp the checkpoint.""" # It is possible save checkpoints faster than 1 per second resulting in # collisions in the naming convention. - cur_time = time.time() for _ in xrange(10): - final_dir = os.path.join(self.config.backup_dir, str(cur_time)) + timestamp = self._checkpoint_timestamp() + final_dir = os.path.join(self.config.backup_dir, timestamp) try: os.rename(self.config.in_progress_dir, final_dir) return except OSError: - # It is possible if the checkpoints are made extremely quickly - # that will result in a name collision. - # If so, increment and try again - cur_time += .01 + logger.warning("Extreme, unexpected race condition, retrying (%s)", timestamp) # After 10 attempts... something is probably wrong here... logger.error( From 40e877750090ad3e22adb0eb15a1e0fc6bafa9fb Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 8 Apr 2016 16:04:47 -0700 Subject: [PATCH 196/317] reverter.finalize_checkpoint() : handle empty checkpoints Should fix #1243 --- letsencrypt/reverter.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index ea54a91ee..0ed75df6f 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -481,30 +481,32 @@ class Reverter(object): checkpoint is not able to be finalized. """ - # Adds title to self.config.in_progress_dir CHANGES_SINCE - # Move self.config.in_progress_dir to Backups directory and - # rename the directory as a timestamp # Check to make sure an "in progress" directory exists if not os.path.isdir(self.config.in_progress_dir): return - changes_since_path = os.path.join( - self.config.in_progress_dir, "CHANGES_SINCE") + changes_since_path = os.path.join(self.config.in_progress_dir, "CHANGES_SINCE") + changes_since_tmp_path = os.path.join(self.config.in_progress_dir, "CHANGES_SINCE.tmp") - changes_since_tmp_path = os.path.join( - self.config.in_progress_dir, "CHANGES_SINCE.tmp") + if not os.path.exists(self.config.changes_since_path): + logger.info("Rollback checkpoint is empty (no changes made?)") + with open(self.config.changes_since_path) as f: + f.write("No changes\n") + # Add title to self.config.in_progress_dir CHANGES_SINCE try: with open(changes_since_tmp_path, "w") as changes_tmp: changes_tmp.write("-- %s --\n" % title) with open(changes_since_path, "r") as changes_orig: changes_tmp.write(changes_orig.read()) + # Move self.config.in_progress_dir to Backups directory shutil.move(changes_since_tmp_path, changes_since_path) except (IOError, OSError): logger.error("Unable to finalize checkpoint - adding title") raise errors.ReverterError("Unable to add title") + # rename the directory as a timestamp self._timestamp_progress_dir() def _timestamp_progress_dir(self): From cf4f97bbbfac73184d766a68e821f7ffa9a2ae24 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 8 Apr 2016 16:15:43 -0700 Subject: [PATCH 197/317] typofix --- letsencrypt/reverter.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index c0771d577..5844ae6c2 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -502,22 +502,22 @@ class Reverter(object): shutil.move(changes_since_tmp_path, changes_since_path) except (IOError, OSError): logger.error("Unable to finalize checkpoint - adding title") - logger.debug("Exception was:\%s", traceback.format_exc()) + logger.debug("Exception was:\n%s", traceback.format_exc()) raise errors.ReverterError("Unable to add title") self._timestamp_progress_dir() - def _checkpoint_timestamp(self) + def _checkpoint_timestamp(self): "Determine the timestamp of the checkpoint, enforcing monotonicity." timestamp = str(time.time()) others = os.listdir(self.config.backup_dir) - others.append(new_dir) + others.append(timestamp) others.sort() if others[-1] != timestamp: timetravel = str(float(others[-1]) + 1) logger.warn("Current timestamp %s does not correspond to newest reverter " "checkpoint; your clock probably jumped. Time travelling to %s", - new_dir, timetravel) + timestamp, timetravel) timestamp = timetravel elif len(others) > 1 and others[-2] == timestamp: # It is possible if the checkpoints are made extremely quickly From 5e971a5e5a9a7dd8baed9a61091810f3ef4bc1ac Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 8 Apr 2016 16:38:40 -0700 Subject: [PATCH 198/317] Check the right path --- letsencrypt/reverter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index 0ed75df6f..b7928220b 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -488,7 +488,7 @@ class Reverter(object): changes_since_path = os.path.join(self.config.in_progress_dir, "CHANGES_SINCE") changes_since_tmp_path = os.path.join(self.config.in_progress_dir, "CHANGES_SINCE.tmp") - if not os.path.exists(self.config.changes_since_path): + if not os.path.exists(changes_since_path): logger.info("Rollback checkpoint is empty (no changes made?)") with open(self.config.changes_since_path) as f: f.write("No changes\n") From 8145b7c11b9f824831ef2f059eaafdb664307d90 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sat, 9 Apr 2016 08:15:24 +0000 Subject: [PATCH 199/317] ACME: omitempty Error.detail, Error.type (fixes #2289) --- acme/acme/messages.py | 4 ++-- acme/acme/messages_test.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 24a3b580c..40ddbdb2f 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -37,9 +37,9 @@ class Error(jose.JSONObjectWithFields, errors.Error): ) ) - typ = jose.Field('type') + typ = jose.Field('type', omitempty=True, default='about:blank') title = jose.Field('title', omitempty=True) - detail = jose.Field('detail') + detail = jose.Field('detail', omitempty=True) @property def description(self): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index b2b7febdc..c1f027302 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -28,6 +28,14 @@ class ErrorTest(unittest.TestCase): self.error_custom = Error(typ='custom', detail='bar') self.jobj_cusom = {'type': 'custom', 'detail': 'bar'} + def test_default_typ(self): + from acme.messages import Error + self.assertEqual(Error().typ, 'about:blank') + + def test_from_json_empty(self): + from acme.messages import Error + self.assertEqual(Error(), Error.from_json('{}')) + def test_from_json_hashable(self): from acme.messages import Error hash(Error.from_json(self.error.to_json())) From 0839168de7da3950e93056c2f80d029e7225e43f Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Apr 2016 07:50:39 +0000 Subject: [PATCH 200/317] Fake deserialization error in test_check_response_not_ok_jobj_no_error --- acme/acme/client_test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index c8e95a423..7403cde81 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -484,9 +484,11 @@ class ClientNetworkTest(unittest.TestCase): def test_check_response_not_ok_jobj_no_error(self): self.response.ok = False self.response.json.return_value = {} - # pylint: disable=protected-access - self.assertRaises( - errors.ClientError, self.net._check_response, self.response) + with mock.patch('acme.client.messages.Error.from_json') as from_json: + from_json.side_effect = jose.DeserializationError + # pylint: disable=protected-access + self.assertRaises( + errors.ClientError, self.net._check_response, self.response) def test_check_response_not_ok_jobj_error(self): self.response.ok = False From df2baae4760fe122e4bbc727465472e833458867 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Apr 2016 17:57:08 +0000 Subject: [PATCH 201/317] apacheconf: sane sudo letsencrypt (fixes #2800) - hardcoded `LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/apacheconftest/bin/letsencrypt` causes Travis tests to fail if running under any other Travis user (from e.g. a fork) - `sudo env "PATH=$PATH" letsencrypt` should make sure that sudo can find letsencrypt binary from virtualenv; realpath is not necessary - sudo is called already from within the test script, no need to sudo the entire script --- .travis.yml | 1 - .../tests/apache-conf-files/apache-conf-test | 4 +--- tox.ini | 4 +--- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index f9706e263..9024defcd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,7 +110,6 @@ addons: # For Boulder integration testing - rsyslog # for apacheconftest - #- realpath #- apache2 #- libapache2-mod-wsgi #- libapache2-mod-macro diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test index 7b3f83d13..b2623511e 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test @@ -5,9 +5,7 @@ export EA=/etc/apache2/ TESTDIR="`dirname $0`" -LEROOT="`realpath \"$TESTDIR/../../../../\"`" cd $TESTDIR/passing -LETSENCRYPT="${LETSENCRYPT:-$LEROOT/venv/bin/letsencrypt}" function CleanupExit() { echo control c, exiting tests... @@ -61,7 +59,7 @@ trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... Setup - RESULT=`echo c | sudo "$LETSENCRYPT" -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo env "PATH=$PATH" letsencrypt -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then echo passed else diff --git a/tox.ini b/tox.ini index 6af9610e3..bac1405e6 100644 --- a/tox.ini +++ b/tox.ini @@ -77,11 +77,9 @@ commands = [testenv:apacheconftest] #basepython = python2.7 -setenv = - LETSENCRYPT=/home/travis/build/letsencrypt/letsencrypt/.tox/apacheconftest/bin/letsencrypt commands = pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt - sudo ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules + ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules [testenv:le_auto] # At the moment, this tests under Python 2.7 only, as only that version is From 3516b7088445c0daa51781081b3683b216b6a323 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Apr 2016 18:44:28 +0000 Subject: [PATCH 202/317] apacheconftest: toxinidir instead of . --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bac1405e6..5768733b5 100644 --- a/tox.ini +++ b/tox.ini @@ -79,7 +79,8 @@ commands = #basepython = python2.7 commands = pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt - ./letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules + {toxinidir}/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules + [testenv:le_auto] # At the moment, this tests under Python 2.7 only, as only that version is From 9b8363acfac4cd0aac2db9fce1e36137a3ccfb3b Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Sun, 10 Apr 2016 18:52:38 +0000 Subject: [PATCH 203/317] Fix `sudo echo`... --- .../tests/apache-conf-files/apache-conf-test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test index b2623511e..5725508e9 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test @@ -19,13 +19,13 @@ function Setup() { if [ "$APPEND_APACHECONF" = "" ] ; then sudo cp "$f" "$EA"/sites-available/ sudo ln -sf "$EA/sites-available/$f" "$EA/sites-enabled/$f" - sudo echo """ + echo " ServerName example.com DocumentRoot /tmp/ ErrorLog /tmp/error.log CustomLog /tmp/requests.log combined -""" >> $EA/sites-available/throwaway-example.conf +" | sudo tee $EA/sites-available/throwaway-example.conf >/dev/null else TMP="/tmp/`basename \"$APPEND_APACHECONF\"`.$$" sudo cp -a "$APPEND_APACHECONF" "$TMP" From 9111faeb942129cf2831be3ae695b5e7b5bc5b06 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 11 Apr 2016 09:44:29 -0700 Subject: [PATCH 204/317] don't lose domain ordering --- letsencrypt/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 221879080..508117489 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -245,8 +245,9 @@ class Client(object): domains, self.config.allow_subset_of_names) - domains = [a.body.identifier.value.encode('ascii') - for a in authzr] + auth_domains = set(a.body.identifier.value.encode('ascii') + for a in authzr) + domains = [d for d in domains if d in auth_domains] # Create CSR from names key = crypto_util.init_save_key( From f2e266cefd78b8c685983343fd5e54cdd1ff6e09 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 11 Apr 2016 13:23:05 -0700 Subject: [PATCH 205/317] Only count actual checkpoints for ordering purposes --- letsencrypt/reverter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index 5844ae6c2..7feae966c 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -1,5 +1,6 @@ """Reverter class saves configuration checkpoints and allows for recovery.""" import csv +import glob import logging import os import shutil @@ -510,7 +511,7 @@ class Reverter(object): def _checkpoint_timestamp(self): "Determine the timestamp of the checkpoint, enforcing monotonicity." timestamp = str(time.time()) - others = os.listdir(self.config.backup_dir) + others = glob.glob(os.path.join(self.config.backup_dir, "[0-9]*")) others.append(timestamp) others.sort() if others[-1] != timestamp: From a6235c069ac6f4a2ebf90d3671014eeff3630e30 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 11 Apr 2016 13:36:01 -0700 Subject: [PATCH 206/317] glob requires basename()ing --- letsencrypt/reverter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/reverter.py b/letsencrypt/reverter.py index 7feae966c..1ee12a561 100644 --- a/letsencrypt/reverter.py +++ b/letsencrypt/reverter.py @@ -512,6 +512,7 @@ class Reverter(object): "Determine the timestamp of the checkpoint, enforcing monotonicity." timestamp = str(time.time()) others = glob.glob(os.path.join(self.config.backup_dir, "[0-9]*")) + others = [os.path.basename(d) for d in others] others.append(timestamp) others.sort() if others[-1] != timestamp: @@ -533,7 +534,7 @@ class Reverter(object): # It is possible save checkpoints faster than 1 per second resulting in # collisions in the naming convention. - for _ in xrange(10): + for _ in xrange(2): timestamp = self._checkpoint_timestamp() final_dir = os.path.join(self.config.backup_dir, timestamp) try: From 9008adc1760e889093b618e133caaba0279a18d5 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 12 Apr 2016 12:23:24 -0700 Subject: [PATCH 207/317] add test to prevent regressions --- letsencrypt/tests/client_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/tests/client_test.py b/letsencrypt/tests/client_test.py index cd6b11158..49fa5b17e 100644 --- a/letsencrypt/tests/client_test.py +++ b/letsencrypt/tests/client_test.py @@ -201,7 +201,8 @@ class ClientTest(unittest.TestCase): authzr = [] - for domain in domains: + # domain ordering should not be affected by authorization order + for domain in reversed(domains): authzr.append( mock.MagicMock( body=mock.MagicMock( From 714282c82c9007c106b8f71ae43c3d377c49027d Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 10:04:22 -0700 Subject: [PATCH 208/317] Add boulder host --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9024defcd..38c874279 100644 --- a/.travis.yml +++ b/.travis.yml @@ -89,6 +89,7 @@ addons: - le2.wtf - le3.wtf - nginx.wtf + - boulder - boulder-mysql - boulder-rabbitmq mariadb: "10.0" From 3961b70debeb228de88751fb4c4cad0058fcd047 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:03:59 -0700 Subject: [PATCH 209/317] s/letsencrypt/certbot --- {letsencrypt => certbot}/.gitignore | 0 {letsencrypt => certbot}/__init__.py | 0 {letsencrypt => certbot}/account.py | 6 +- {letsencrypt => certbot}/achallenges.py | 2 +- {letsencrypt => certbot}/auth_handler.py | 24 +-- {letsencrypt => certbot}/cli.py | 32 ++-- {letsencrypt => certbot}/client.py | 42 ++--- {letsencrypt => certbot}/colored_logging.py | 2 +- {letsencrypt => certbot}/configuration.py | 16 +- {letsencrypt => certbot}/constants.py | 4 +- {letsencrypt => certbot}/crypto_util.py | 16 +- {letsencrypt => certbot}/display/__init__.py | 0 {letsencrypt => certbot}/display/completer.py | 2 +- .../display/dummy_readline.py | 0 .../display/enhancements.py | 8 +- {letsencrypt => certbot}/display/ops.py | 12 +- {letsencrypt => certbot}/display/util.py | 6 +- {letsencrypt => certbot}/error_handler.py | 0 {letsencrypt => certbot}/errors.py | 0 {letsencrypt => certbot}/hooks.py | 2 +- {letsencrypt => certbot}/interfaces.py | 12 +- {letsencrypt => certbot}/le_util.py | 2 +- {letsencrypt => certbot}/log.py | 2 +- {letsencrypt => certbot}/main.py | 46 ++--- {letsencrypt => certbot}/notify.py | 0 {letsencrypt => certbot}/plugins/__init__.py | 0 {letsencrypt => certbot}/plugins/common.py | 8 +- .../plugins/common_test.py | 38 ++-- {letsencrypt => certbot}/plugins/disco.py | 12 +- .../plugins/disco_test.py | 28 +-- {letsencrypt => certbot}/plugins/manual.py | 12 +- .../plugins/manual_test.py | 34 ++-- {letsencrypt => certbot}/plugins/null.py | 4 +- {letsencrypt => certbot}/plugins/null_test.py | 6 +- {letsencrypt => certbot}/plugins/selection.py | 14 +- .../plugins/selection_test.py | 32 ++-- .../plugins/standalone.py | 10 +- .../plugins/standalone_test.py | 28 +-- {letsencrypt => certbot}/plugins/util.py | 2 +- {letsencrypt => certbot}/plugins/util_test.py | 32 ++-- {letsencrypt => certbot}/plugins/webroot.py | 10 +- .../plugins/webroot_test.py | 30 ++-- {letsencrypt => certbot}/renewal.py | 26 +-- {letsencrypt => certbot}/reporter.py | 4 +- {letsencrypt => certbot}/reverter.py | 24 +-- {letsencrypt => certbot}/storage.py | 16 +- {letsencrypt => certbot}/tests/__init__.py | 0 .../tests/account_test.py | 40 ++--- {letsencrypt => certbot}/tests/acme_util.py | 2 +- .../tests/auth_handler_test.py | 54 +++--- {letsencrypt => certbot}/tests/cli_test.py | 170 +++++++++--------- {letsencrypt => certbot}/tests/client_test.py | 82 ++++----- .../tests/colored_logging_test.py | 8 +- .../tests/configuration_test.py | 24 +-- .../tests/crypto_util_test.py | 76 ++++---- .../tests/display/__init__.py | 0 .../tests/display/completer_test.py | 14 +- .../tests/display/enhancements_test.py | 16 +- .../tests/display/ops_test.py | 68 +++---- .../tests/display/util_test.py | 40 ++--- .../tests/error_handler_test.py | 10 +- {letsencrypt => certbot}/tests/errors_test.py | 14 +- {letsencrypt => certbot}/tests/hook_test.py | 20 +-- .../tests/le_util_test.py | 70 ++++---- {letsencrypt => certbot}/tests/log_test.py | 4 +- {letsencrypt => certbot}/tests/notify_test.py | 18 +- .../tests/reporter_test.py | 6 +- .../tests/reverter_test.py | 36 ++-- .../tests/storage_test.py | 60 +++---- {letsencrypt => certbot}/tests/test_util.py | 0 .../testdata/archive/sample-renewal/cert1.pem | 0 .../archive/sample-renewal/chain1.pem | 0 .../archive/sample-renewal/fullchain1.pem | 0 .../archive/sample-renewal/privkey1.pem | 0 .../tests/testdata/cert-san.pem | 0 .../tests/testdata/cert.b64jose | 0 .../tests/testdata/cert.der | Bin .../tests/testdata/cert.pem | 0 .../tests/testdata/cli.ini | 0 .../tests/testdata/csr-6sans.pem | 0 .../tests/testdata/csr-nosans.pem | 0 .../tests/testdata/csr-san.der | Bin .../tests/testdata/csr-san.pem | 0 .../tests/testdata/csr.der | Bin .../tests/testdata/csr.pem | 0 .../tests/testdata/dsa512_key.pem | 0 .../tests/testdata/dsa_cert.pem | 0 .../testdata/live/sample-renewal/cert.pem | 0 .../testdata/live/sample-renewal/chain.pem | 0 .../live/sample-renewal/fullchain.pem | 0 .../testdata/live/sample-renewal/privkey.pem | 0 .../tests/testdata/matching_cert.pem | 0 .../tests/testdata/rsa256_key.pem | 0 .../tests/testdata/rsa512_key.pem | 0 .../tests/testdata/rsa512_key_2.pem | 0 .../testdata/sample-renewal-ancient.conf | 0 .../tests/testdata/sample-renewal.conf | 0 .../tests/testdata/webrootconftest.ini | 0 pep8.travis.sh | 2 +- setup.py | 20 +-- tox.cover.sh | 4 +- tox.ini | 6 +- 102 files changed, 734 insertions(+), 736 deletions(-) rename {letsencrypt => certbot}/.gitignore (100%) rename {letsencrypt => certbot}/__init__.py (100%) rename {letsencrypt => certbot}/account.py (98%) rename {letsencrypt => certbot}/achallenges.py (97%) rename {letsencrypt => certbot}/auth_handler.py (96%) rename {letsencrypt => certbot}/cli.py (98%) rename {letsencrypt => certbot}/client.py (95%) rename {letsencrypt => certbot}/colored_logging.py (97%) rename {letsencrypt => certbot}/configuration.py (92%) rename {letsencrypt => certbot}/constants.py (95%) rename {letsencrypt => certbot}/crypto_util.py (96%) rename {letsencrypt => certbot}/display/__init__.py (100%) rename {letsencrypt => certbot}/display/completer.py (97%) rename {letsencrypt => certbot}/display/dummy_readline.py (100%) rename {letsencrypt => certbot}/display/enhancements.py (88%) rename {letsencrypt => certbot}/display/ops.py (96%) rename {letsencrypt => certbot}/display/util.py (99%) rename {letsencrypt => certbot}/error_handler.py (100%) rename {letsencrypt => certbot}/errors.py (100%) rename {letsencrypt => certbot}/hooks.py (99%) rename {letsencrypt => certbot}/interfaces.py (98%) rename {letsencrypt => certbot}/le_util.py (99%) rename {letsencrypt => certbot}/log.py (97%) rename {letsencrypt => certbot}/main.py (96%) rename {letsencrypt => certbot}/notify.py (100%) rename {letsencrypt => certbot}/plugins/__init__.py (100%) rename {letsencrypt => certbot}/plugins/common.py (98%) rename {letsencrypt => certbot}/plugins/common_test.py (87%) rename {letsencrypt => certbot}/plugins/disco.py (97%) rename {letsencrypt => certbot}/plugins/disco_test.py (92%) rename {letsencrypt => certbot}/plugins/manual.py (96%) rename {letsencrypt => certbot}/plugins/manual_test.py (78%) rename {letsencrypt => certbot}/plugins/null.py (94%) rename {letsencrypt => certbot}/plugins/null_test.py (77%) rename {letsencrypt => certbot}/plugins/selection.py (96%) rename {letsencrypt => certbot}/plugins/selection_test.py (80%) rename {letsencrypt => certbot}/plugins/standalone.py (97%) rename {letsencrypt => certbot}/plugins/standalone_test.py (91%) rename {letsencrypt => certbot}/plugins/util.py (98%) rename {letsencrypt => certbot}/plugins/util_test.py (81%) rename {letsencrypt => certbot}/plugins/webroot.py (98%) rename {letsencrypt => certbot}/plugins/webroot_test.py (92%) rename {letsencrypt => certbot}/renewal.py (96%) rename {letsencrypt => certbot}/reporter.py (98%) rename {letsencrypt => certbot}/reverter.py (97%) rename {letsencrypt => certbot}/storage.py (99%) rename {letsencrypt => certbot}/tests/__init__.py (100%) rename {letsencrypt => certbot}/tests/account_test.py (82%) rename {letsencrypt => certbot}/tests/acme_util.py (98%) rename {letsencrypt => certbot}/tests/auth_handler_test.py (90%) rename {letsencrypt => certbot}/tests/cli_test.py (88%) rename {letsencrypt => certbot}/tests/client_test.py (88%) rename {letsencrypt => certbot}/tests/colored_logging_test.py (85%) rename {letsencrypt => certbot}/tests/configuration_test.py (87%) rename {letsencrypt => certbot}/tests/crypto_util_test.py (74%) rename {letsencrypt => certbot}/tests/display/__init__.py (100%) rename {letsencrypt => certbot}/tests/display/completer_test.py (88%) rename {letsencrypt => certbot}/tests/display/enhancements_test.py (74%) rename {letsencrypt => certbot}/tests/display/ops_test.py (85%) rename {letsencrypt => certbot}/tests/display/util_test.py (91%) rename {letsencrypt => certbot}/tests/error_handler_test.py (90%) rename {letsencrypt => certbot}/tests/errors_test.py (74%) rename {letsencrypt => certbot}/tests/hook_test.py (86%) rename {letsencrypt => certbot}/tests/le_util_test.py (83%) rename {letsencrypt => certbot}/tests/log_test.py (95%) rename {letsencrypt => certbot}/tests/notify_test.py (79%) rename {letsencrypt => certbot}/tests/reporter_test.py (95%) rename {letsencrypt => certbot}/tests/reverter_test.py (94%) rename {letsencrypt => certbot}/tests/storage_test.py (96%) rename {letsencrypt => certbot}/tests/test_util.py (100%) rename {letsencrypt => certbot}/tests/testdata/archive/sample-renewal/cert1.pem (100%) rename {letsencrypt => certbot}/tests/testdata/archive/sample-renewal/chain1.pem (100%) rename {letsencrypt => certbot}/tests/testdata/archive/sample-renewal/fullchain1.pem (100%) rename {letsencrypt => certbot}/tests/testdata/archive/sample-renewal/privkey1.pem (100%) rename {letsencrypt => certbot}/tests/testdata/cert-san.pem (100%) rename {letsencrypt => certbot}/tests/testdata/cert.b64jose (100%) rename {letsencrypt => certbot}/tests/testdata/cert.der (100%) rename {letsencrypt => certbot}/tests/testdata/cert.pem (100%) rename {letsencrypt => certbot}/tests/testdata/cli.ini (100%) rename {letsencrypt => certbot}/tests/testdata/csr-6sans.pem (100%) rename {letsencrypt => certbot}/tests/testdata/csr-nosans.pem (100%) rename {letsencrypt => certbot}/tests/testdata/csr-san.der (100%) rename {letsencrypt => certbot}/tests/testdata/csr-san.pem (100%) rename {letsencrypt => certbot}/tests/testdata/csr.der (100%) rename {letsencrypt => certbot}/tests/testdata/csr.pem (100%) rename {letsencrypt => certbot}/tests/testdata/dsa512_key.pem (100%) rename {letsencrypt => certbot}/tests/testdata/dsa_cert.pem (100%) rename {letsencrypt => certbot}/tests/testdata/live/sample-renewal/cert.pem (100%) rename {letsencrypt => certbot}/tests/testdata/live/sample-renewal/chain.pem (100%) rename {letsencrypt => certbot}/tests/testdata/live/sample-renewal/fullchain.pem (100%) rename {letsencrypt => certbot}/tests/testdata/live/sample-renewal/privkey.pem (100%) rename {letsencrypt => certbot}/tests/testdata/matching_cert.pem (100%) rename {letsencrypt => certbot}/tests/testdata/rsa256_key.pem (100%) rename {letsencrypt => certbot}/tests/testdata/rsa512_key.pem (100%) rename {letsencrypt => certbot}/tests/testdata/rsa512_key_2.pem (100%) rename {letsencrypt => certbot}/tests/testdata/sample-renewal-ancient.conf (100%) rename {letsencrypt => certbot}/tests/testdata/sample-renewal.conf (100%) rename {letsencrypt => certbot}/tests/testdata/webrootconftest.ini (100%) diff --git a/letsencrypt/.gitignore b/certbot/.gitignore similarity index 100% rename from letsencrypt/.gitignore rename to certbot/.gitignore diff --git a/letsencrypt/__init__.py b/certbot/__init__.py similarity index 100% rename from letsencrypt/__init__.py rename to certbot/__init__.py diff --git a/letsencrypt/account.py b/certbot/account.py similarity index 98% rename from letsencrypt/account.py rename to certbot/account.py index 464d07b18..8c1d55177 100644 --- a/letsencrypt/account.py +++ b/certbot/account.py @@ -14,9 +14,9 @@ from acme import fields as acme_fields from acme import jose from acme import messages -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import errors +from certbot import interfaces +from certbot import le_util logger = logging.getLogger(__name__) diff --git a/letsencrypt/achallenges.py b/certbot/achallenges.py similarity index 97% rename from letsencrypt/achallenges.py rename to certbot/achallenges.py index 0cdec06df..5ee6d2945 100644 --- a/letsencrypt/achallenges.py +++ b/certbot/achallenges.py @@ -6,7 +6,7 @@ and :class:`.ChallengeBody` (denoted by ``challb``):: from acme import challenges from acme import messages - from letsencrypt import achallenges + from certbot import achallenges chall = challenges.DNS(token='foo') challb = messages.ChallengeBody(chall=chall) diff --git a/letsencrypt/auth_handler.py b/certbot/auth_handler.py similarity index 96% rename from letsencrypt/auth_handler.py rename to certbot/auth_handler.py index 658315597..377747772 100644 --- a/letsencrypt/auth_handler.py +++ b/certbot/auth_handler.py @@ -8,10 +8,10 @@ import zope.component from acme import challenges from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors -from letsencrypt import error_handler -from letsencrypt import interfaces +from certbot import achallenges +from certbot import errors +from certbot import error_handler +from certbot import interfaces logger = logging.getLogger(__name__) @@ -22,17 +22,17 @@ class AuthHandler(object): :ivar auth: Authenticator capable of solving :class:`~acme.challenges.Challenge` types - :type auth: :class:`letsencrypt.interfaces.IAuthenticator` + :type auth: :class:`certbot.interfaces.IAuthenticator` :ivar acme.client.Client acme: ACME client API. :ivar account: Client's Account - :type account: :class:`letsencrypt.account.Account` + :type account: :class:`certbot.account.Account` :ivar dict authzr: ACME Authorization Resource dict where keys are domains and values are :class:`acme.messages.AuthorizationResource` :ivar list achalls: DV challenges in the form of - :class:`letsencrypt.achallenges.AnnotatedChallenge` + :class:`certbot.achallenges.AnnotatedChallenge` """ def __init__(self, auth, acme, account): @@ -287,7 +287,7 @@ class AuthHandler(object): :param list path: List of indices from `challenges`. :returns: achalls, list of challenge type - :class:`letsencrypt.achallenges.Indexed` + :class:`certbot.achallenges.Indexed` :rtype: list :raises .errors.Error: if challenge type is not recognized @@ -310,7 +310,7 @@ def challb_to_achall(challb, account_key, domain): :param str domain: Domain of the challb :returns: Appropriate AnnotatedChallenge - :rtype: :class:`letsencrypt.achallenges.AnnotatedChallenge` + :rtype: :class:`certbot.achallenges.AnnotatedChallenge` """ chall = challb.chall @@ -347,7 +347,7 @@ def gen_challenge_path(challbs, preferences, combinations): :returns: tuple of indices from ``challenges``. :rtype: tuple - :raises letsencrypt.errors.AuthorizationError: If a + :raises certbot.errors.AuthorizationError: If a path cannot be created that satisfies the CA given the preferences and combinations. @@ -463,7 +463,7 @@ def _report_failed_challs(failed_achalls): """Notifies the user about failed challenges. :param set failed_achalls: A set of failed - :class:`letsencrypt.achallenges.AnnotatedChallenge`. + :class:`certbot.achallenges.AnnotatedChallenge`. """ problems = dict() @@ -481,7 +481,7 @@ def _generate_failed_chall_msg(failed_achalls): """Creates a user friendly error message about failed challenges. :param list failed_achalls: A list of failed - :class:`letsencrypt.achallenges.AnnotatedChallenge` with the same error + :class:`certbot.achallenges.AnnotatedChallenge` with the same error type. :returns: A formatted error message for the client. diff --git a/letsencrypt/cli.py b/certbot/cli.py similarity index 98% rename from letsencrypt/cli.py rename to certbot/cli.py index 2dc3de3d3..ebb9a6ca2 100644 --- a/letsencrypt/cli.py +++ b/certbot/cli.py @@ -12,17 +12,17 @@ import configargparse import OpenSSL import six -import letsencrypt +import certbot -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import hooks -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import hooks +from certbot import interfaces +from certbot import le_util -from letsencrypt.plugins import disco as plugins_disco -import letsencrypt.plugins.selection as plugin_selection +from certbot.plugins import disco as plugins_disco +import certbot.plugins.selection as plugin_selection logger = logging.getLogger(__name__) @@ -31,14 +31,14 @@ helpful_parser = None # For help strings, figure out how the user ran us. # When invoked from letsencrypt-auto, sys.argv[0] is something like: -# "/home/user/.local/share/letsencrypt/bin/letsencrypt" +# "/home/user/.local/share/certbot/bin/certbot" # Note that this won't work if the user set VENV_PATH or XDG_DATA_HOME before # running letsencrypt-auto (and sudo stops us from seeing if they did), so it # should only be used for purposes where inability to detect letsencrypt-auto # fails safely -fragment = os.path.join(".local", "share", "letsencrypt") -cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "letsencrypt" +fragment = os.path.join(".local", "share", "certbot") +cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "certbot" # Argparse's help formatting has a lot of unhelpful peculiarities, so we want # to replace as much of it as we can... @@ -63,7 +63,7 @@ the cert. Major SUBCOMMANDS are: """.format(cli_command) -# This is the short help for letsencrypt --help, where we disable argparse +# This is the short help for certbot --help, where we disable argparse # altogether USAGE = SHORT_USAGE + """Choice of server plugins for obtaining and installing cert: @@ -256,12 +256,12 @@ class HelpfulArgumentParser(object): This class wraps argparse, adding the ability to make --help less verbose, and request help on specific subcategories at a time, eg - 'letsencrypt --help security' for security options. + 'certbot --help security' for security options. """ def __init__(self, args, plugins, detect_defaults=False): - from letsencrypt import main + from certbot import main self.VERBS = {"auth": main.obtain_cert, "certonly": main.obtain_cert, "config_changes": main.config_changes, "run": main.run, "install": main.install, "plugins": main.plugins_cmd, @@ -616,7 +616,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "always expand and replace it with the additional names.") helpful.add( "automation", "--version", action="version", - version="%(prog)s {0}".format(letsencrypt.__version__), + version="%(prog)s {0}".format(certbot.__version__), help="show program's version number and exit") helpful.add( "automation", "--force-renewal", "--renew-by-default", diff --git a/letsencrypt/client.py b/certbot/client.py similarity index 95% rename from letsencrypt/client.py rename to certbot/client.py index 221879080..1ca301c1e 100644 --- a/letsencrypt/client.py +++ b/certbot/client.py @@ -11,23 +11,23 @@ from acme import client as acme_client from acme import jose from acme import messages -import letsencrypt +import certbot -from letsencrypt import account -from letsencrypt import auth_handler -from letsencrypt import configuration -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import error_handler -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import reverter -from letsencrypt import storage +from certbot import account +from certbot import auth_handler +from certbot import configuration +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import error_handler +from certbot import interfaces +from certbot import le_util +from certbot import reverter +from certbot import storage -from letsencrypt.display import ops as display_ops -from letsencrypt.display import enhancements -from letsencrypt.plugins import selection as plugin_selection +from certbot.display import ops as display_ops +from certbot.display import enhancements +from certbot.plugins import selection as plugin_selection logger = logging.getLogger(__name__) @@ -52,7 +52,7 @@ def _determine_user_agent(config): if config.user_agent is None: ua = "LetsEncryptPythonClient/{0} ({1}) Authenticator/{2} Installer/{3}" - ua = ua.format(letsencrypt.__version__, " ".join(le_util.get_os_info()), + ua = ua.format(certbot.__version__, " ".join(le_util.get_os_info()), config.authenticator, config.installer) else: ua = config.user_agent @@ -87,7 +87,7 @@ def register(config, account_storage, tos_cb=None): None``. This argument is optional, if not supplied it will default to automatic acceptance! - :raises letsencrypt.errors.Error: In case of any client problems, in + :raises certbot.errors.Error: In case of any client problems, in particular registration failure, or unaccepted Terms of Service. :raises acme.errors.Error: In case of any protocol problems. @@ -266,7 +266,7 @@ class Client(object): :param list domains: Domains to request. :param plugins: A PluginsFactory object. - :returns: A new :class:`letsencrypt.storage.RenewableCert` instance + :returns: A new :class:`certbot.storage.RenewableCert` instance referred to the enrolled cert lineage, False if the cert could not be obtained, or None if doing a successful dry run. @@ -492,7 +492,7 @@ def validate_key_csr(privkey, csr=None): If csr is left as None, only the key will be validated. :param privkey: Key associated with CSR - :type privkey: :class:`letsencrypt.le_util.Key` + :type privkey: :class:`certbot.le_util.Key` :param .le_util.CSR csr: CSR @@ -532,7 +532,7 @@ def rollback(default_installer, checkpoints, config, plugins): :param int checkpoints: Number of checkpoints to revert. :param config: Configuration. - :type config: :class:`letsencrypt.interfaces.IConfig` + :type config: :class:`certbot.interfaces.IConfig` """ # Misconfigurations are only a slight problems... allow the user to rollback @@ -554,7 +554,7 @@ def view_config_changes(config, num=None): .. note:: This assumes that the installation is using a Reverter object. :param config: Configuration. - :type config: :class:`letsencrypt.interfaces.IConfig` + :type config: :class:`certbot.interfaces.IConfig` """ rev = reverter.Reverter(config) diff --git a/letsencrypt/colored_logging.py b/certbot/colored_logging.py similarity index 97% rename from letsencrypt/colored_logging.py rename to certbot/colored_logging.py index 443364ddd..d42fb5966 100644 --- a/letsencrypt/colored_logging.py +++ b/certbot/colored_logging.py @@ -2,7 +2,7 @@ import logging import sys -from letsencrypt import le_util +from certbot import le_util class StreamHandler(logging.StreamHandler): diff --git a/letsencrypt/configuration.py b/certbot/configuration.py similarity index 92% rename from letsencrypt/configuration.py rename to certbot/configuration.py index 062722346..e38ff2cfa 100644 --- a/letsencrypt/configuration.py +++ b/certbot/configuration.py @@ -5,10 +5,10 @@ import os from six.moves.urllib import parse # pylint: disable=import-error import zope.interface -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import errors +from certbot import interfaces +from certbot import le_util @zope.interface.implementer(interfaces.IConfig) @@ -16,10 +16,10 @@ class NamespaceConfig(object): """Configuration wrapper around :class:`argparse.Namespace`. For more documentation, including available attributes, please see - :class:`letsencrypt.interfaces.IConfig`. However, note that + :class:`certbot.interfaces.IConfig`. However, note that the following attributes are dynamically resolved using - :attr:`~letsencrypt.interfaces.IConfig.work_dir` and relative - paths defined in :py:mod:`letsencrypt.constants`: + :attr:`~certbot.interfaces.IConfig.work_dir` and relative + paths defined in :py:mod:`certbot.constants`: - `accounts_dir` - `csr_dir` @@ -119,7 +119,7 @@ def check_config_sanity(config): requirements are not met. :param config: IConfig instance holding user configuration - :type args: :class:`letsencrypt.interfaces.IConfig` + :type args: :class:`certbot.interfaces.IConfig` """ # Port check diff --git a/letsencrypt/constants.py b/certbot/constants.py similarity index 95% rename from letsencrypt/constants.py rename to certbot/constants.py index f8ef1e845..09df720b9 100644 --- a/letsencrypt/constants.py +++ b/certbot/constants.py @@ -5,7 +5,7 @@ import logging from acme import challenges -SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins" +SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins" """Setuptools entry point group name for plugins.""" CLI_DEFAULTS = dict( @@ -45,7 +45,7 @@ RENEWER_DEFAULTS = dict( ENHANCEMENTS = ["redirect", "http-header", "ocsp-stapling", "spdy"] -"""List of possible :class:`letsencrypt.interfaces.IInstaller` +"""List of possible :class:`certbot.interfaces.IInstaller` enhancements. List of expected options parameters: diff --git a/letsencrypt/crypto_util.py b/certbot/crypto_util.py similarity index 96% rename from letsencrypt/crypto_util.py rename to certbot/crypto_util.py index 5fdcba843..1b4907bfa 100644 --- a/letsencrypt/crypto_util.py +++ b/certbot/crypto_util.py @@ -14,16 +14,16 @@ import zope.component from acme import crypto_util as acme_crypto_util from acme import jose -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import errors +from certbot import interfaces +from certbot import le_util logger = logging.getLogger(__name__) # High level functions -def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): +def init_save_key(key_size, key_dir, keyname="key-certbot.pem"): """Initializes and saves a privkey. Inits key and saves it in PEM format on the filesystem. @@ -36,7 +36,7 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): :param str keyname: Filename of key :returns: Key - :rtype: :class:`letsencrypt.le_util.Key` + :rtype: :class:`certbot.le_util.Key` :raises ValueError: If unable to generate the key given key_size. @@ -61,18 +61,18 @@ def init_save_key(key_size, key_dir, keyname="key-letsencrypt.pem"): return le_util.Key(key_path, key_pem) -def init_save_csr(privkey, names, path, csrname="csr-letsencrypt.pem"): +def init_save_csr(privkey, names, path, csrname="csr-certbot.pem"): """Initialize a CSR with the given private key. :param privkey: Key to include in the CSR - :type privkey: :class:`letsencrypt.le_util.Key` + :type privkey: :class:`certbot.le_util.Key` :param set names: `str` names to include in the CSR :param str path: Certificate save directory. :returns: CSR - :rtype: :class:`letsencrypt.le_util.CSR` + :rtype: :class:`certbot.le_util.CSR` """ csr_pem, csr_der = make_csr(privkey.pem, names) diff --git a/letsencrypt/display/__init__.py b/certbot/display/__init__.py similarity index 100% rename from letsencrypt/display/__init__.py rename to certbot/display/__init__.py diff --git a/letsencrypt/display/completer.py b/certbot/display/completer.py similarity index 97% rename from letsencrypt/display/completer.py rename to certbot/display/completer.py index fed476bb3..37564954a 100644 --- a/letsencrypt/display/completer.py +++ b/certbot/display/completer.py @@ -4,7 +4,7 @@ import glob try: import readline except ImportError: - import letsencrypt.display.dummy_readline as readline + import certbot.display.dummy_readline as readline class Completer(object): diff --git a/letsencrypt/display/dummy_readline.py b/certbot/display/dummy_readline.py similarity index 100% rename from letsencrypt/display/dummy_readline.py rename to certbot/display/dummy_readline.py diff --git a/letsencrypt/display/enhancements.py b/certbot/display/enhancements.py similarity index 88% rename from letsencrypt/display/enhancements.py rename to certbot/display/enhancements.py index 39def1651..e7432a91e 100644 --- a/letsencrypt/display/enhancements.py +++ b/certbot/display/enhancements.py @@ -3,9 +3,9 @@ import logging import zope.component -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.display import util as display_util +from certbot import errors +from certbot import interfaces +from certbot.display import util as display_util logger = logging.getLogger(__name__) @@ -18,7 +18,7 @@ def ask(enhancement): """Display the enhancement to the user. :param str enhancement: One of the - :class:`letsencrypt.CONFIG.ENHANCEMENTS` enhancements + :class:`certbot.CONFIG.ENHANCEMENTS` enhancements :returns: True if feature is desired, False otherwise :rtype: bool diff --git a/letsencrypt/display/ops.py b/certbot/display/ops.py similarity index 96% rename from letsencrypt/display/ops.py rename to certbot/display/ops.py index 302051b1b..6752bf0c1 100644 --- a/letsencrypt/display/ops.py +++ b/certbot/display/ops.py @@ -4,10 +4,10 @@ import os import zope.component -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt.display import util as display_util +from certbot import errors +from certbot import interfaces +from certbot import le_util +from certbot.display import util as display_util logger = logging.getLogger(__name__) @@ -56,7 +56,7 @@ def choose_account(accounts): """Choose an account. :param list accounts: Containing at least one - :class:`~letsencrypt.account.Account` + :class:`~certbot.account.Account` """ # Note this will get more complicated once we start recording authorizations @@ -74,7 +74,7 @@ def choose_names(installer): """Display screen to select domains to validate. :param installer: An installer object - :type installer: :class:`letsencrypt.interfaces.IInstaller` + :type installer: :class:`certbot.interfaces.IInstaller` :returns: List of selected names :rtype: `list` of `str` diff --git a/letsencrypt/display/util.py b/certbot/display/util.py similarity index 99% rename from letsencrypt/display/util.py rename to certbot/display/util.py index 20c6be156..f01c4bc12 100644 --- a/letsencrypt/display/util.py +++ b/certbot/display/util.py @@ -5,9 +5,9 @@ import textwrap import dialog import zope.interface -from letsencrypt import interfaces -from letsencrypt import errors -from letsencrypt.display import completer +from certbot import interfaces +from certbot import errors +from certbot.display import completer WIDTH = 72 HEIGHT = 20 diff --git a/letsencrypt/error_handler.py b/certbot/error_handler.py similarity index 100% rename from letsencrypt/error_handler.py rename to certbot/error_handler.py diff --git a/letsencrypt/errors.py b/certbot/errors.py similarity index 100% rename from letsencrypt/errors.py rename to certbot/errors.py diff --git a/letsencrypt/hooks.py b/certbot/hooks.py similarity index 99% rename from letsencrypt/hooks.py rename to certbot/hooks.py index dce17713d..138e2addc 100644 --- a/letsencrypt/hooks.py +++ b/certbot/hooks.py @@ -6,7 +6,7 @@ import os from subprocess import Popen, PIPE -from letsencrypt import errors +from certbot import errors logger = logging.getLogger(__name__) diff --git a/letsencrypt/interfaces.py b/certbot/interfaces.py similarity index 98% rename from letsencrypt/interfaces.py rename to certbot/interfaces.py index 2fba11869..3eeb6a6f5 100644 --- a/letsencrypt/interfaces.py +++ b/certbot/interfaces.py @@ -51,7 +51,7 @@ class IPluginFactory(zope.interface.Interface): setup( ... entry_points={ - 'letsencrypt.plugins': [ + 'certbot.plugins': [ 'name=example_project.plugin[plugin_deps]', ], }, @@ -154,7 +154,7 @@ class IAuthenticator(IPlugin): """Perform the given challenge. :param list achalls: Non-empty (guaranteed) list of - :class:`~letsencrypt.achallenges.AnnotatedChallenge` + :class:`~certbot.achallenges.AnnotatedChallenge` instances, such that it contains types found within :func:`get_chall_pref` only. @@ -181,7 +181,7 @@ class IAuthenticator(IPlugin): """Revert changes and shutdown after challenges complete. :param list achalls: Non-empty (guaranteed) list of - :class:`~letsencrypt.achallenges.AnnotatedChallenge` + :class:`~certbot.achallenges.AnnotatedChallenge` instances, a subset of those previously passed to :func:`perform`. :raises PluginError: if original configuration cannot be restored @@ -262,10 +262,10 @@ class IInstaller(IPlugin): :param str domain: domain for which to provide enhancement :param str enhancement: An enhancement as defined in - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :param options: Flexible options parameter for enhancement. Check documentation of - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` for expected options for each enhancement. :raises .PluginError: If Enhancement is not supported, or if @@ -277,7 +277,7 @@ class IInstaller(IPlugin): """Returns a list of supported enhancements. :returns: supported enhancements which should be a subset of - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :rtype: :class:`list` of :class:`str` """ diff --git a/letsencrypt/le_util.py b/certbot/le_util.py similarity index 99% rename from letsencrypt/le_util.py rename to certbot/le_util.py index cb1c61074..b9545b2bc 100644 --- a/letsencrypt/le_util.py +++ b/certbot/le_util.py @@ -14,7 +14,7 @@ import sys import configargparse -from letsencrypt import errors +from certbot import errors logger = logging.getLogger(__name__) diff --git a/letsencrypt/log.py b/certbot/log.py similarity index 97% rename from letsencrypt/log.py rename to certbot/log.py index 6436f6fc2..62241254a 100644 --- a/letsencrypt/log.py +++ b/certbot/log.py @@ -3,7 +3,7 @@ import logging import dialog -from letsencrypt.display import util as display_util +from certbot.display import util as display_util class DialogHandler(logging.Handler): # pylint: disable=too-few-public-methods diff --git a/letsencrypt/main.py b/certbot/main.py similarity index 96% rename from letsencrypt/main.py rename to certbot/main.py index 1cb37abe9..c00ad8b59 100644 --- a/letsencrypt/main.py +++ b/certbot/main.py @@ -12,27 +12,27 @@ import zope.component from acme import jose -import letsencrypt +import certbot -from letsencrypt import account -from letsencrypt import client -from letsencrypt import cli -from letsencrypt import crypto_util -from letsencrypt import colored_logging -from letsencrypt import configuration -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import hooks -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import log -from letsencrypt import reporter -from letsencrypt import renewal -from letsencrypt import storage +from certbot import account +from certbot import client +from certbot import cli +from certbot import crypto_util +from certbot import colored_logging +from certbot import configuration +from certbot import constants +from certbot import errors +from certbot import hooks +from certbot import interfaces +from certbot import le_util +from certbot import log +from certbot import reporter +from certbot import renewal +from certbot import storage -from letsencrypt.display import util as display_util, ops as display_ops -from letsencrypt.plugins import disco as plugins_disco -from letsencrypt.plugins import selection as plug_sel +from certbot.display import util as display_util, ops as display_ops +from certbot.plugins import disco as plugins_disco +from certbot.plugins import selection as plug_sel logger = logging.getLogger(__name__) @@ -302,12 +302,12 @@ def _determine_account(config): user input. Same for ``config.email``. :param argparse.Namespace config: CLI arguments - :param letsencrypt.interface.IConfig config: Configuration object + :param certbot.interface.IConfig config: Configuration object :param .AccountStorage account_storage: Account storage. :returns: Account and optionally ACME client API (biproduct of new registration). - :rtype: `tuple` of `letsencrypt.account.Account` and + :rtype: `tuple` of `certbot.account.Account` and `acme.client.Client` """ @@ -605,7 +605,7 @@ def _handle_exception(exc_type, exc_value, trace, config): if issubclass(exc_type, Exception) and (config is None or not config.debug): if config is None: - logfile = "letsencrypt.log" + logfile = "certbot.log" try: with open(logfile, "w") as logfd: traceback.print_exception( @@ -662,7 +662,7 @@ def main(cli_args=sys.argv[1:]): config.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args) setup_logging(config, _cli_log_handler, logfile='letsencrypt.log') - logger.debug("letsencrypt version: %s", letsencrypt.__version__) + logger.debug("certbot version: %s", certbot.__version__) # do not log `config`, as it contains sensitive data (e.g. revoke --key)! logger.debug("Arguments: %r", cli_args) logger.debug("Discovered plugins: %r", plugins) diff --git a/letsencrypt/notify.py b/certbot/notify.py similarity index 100% rename from letsencrypt/notify.py rename to certbot/notify.py diff --git a/letsencrypt/plugins/__init__.py b/certbot/plugins/__init__.py similarity index 100% rename from letsencrypt/plugins/__init__.py rename to certbot/plugins/__init__.py diff --git a/letsencrypt/plugins/common.py b/certbot/plugins/common.py similarity index 98% rename from letsencrypt/plugins/common.py rename to certbot/plugins/common.py index c66857096..757bf19d8 100644 --- a/letsencrypt/plugins/common.py +++ b/certbot/plugins/common.py @@ -10,9 +10,9 @@ import zope.interface from acme.jose import util as jose_util -from letsencrypt import constants -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import interfaces +from certbot import le_util def option_namespace(name): @@ -261,7 +261,7 @@ class TLSSNI01(object): return response -# test utils used by letsencrypt_apache/letsencrypt_nginx (hence +# test utils used by certbot_apache/certbot_nginx (hence # "pragma: no cover") TODO: this might quickly lead to dead code (also # c.f. #383) diff --git a/letsencrypt/plugins/common_test.py b/certbot/plugins/common_test.py similarity index 87% rename from letsencrypt/plugins/common_test.py rename to certbot/plugins/common_test.py index a4292151e..0dd1cd522 100644 --- a/letsencrypt/plugins/common_test.py +++ b/certbot/plugins/common_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.common.""" +"""Tests for certbot.plugins.common.""" import unittest import mock @@ -7,33 +7,33 @@ import OpenSSL from acme import challenges from acme import jose -from letsencrypt import achallenges +from certbot import achallenges -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util class NamespaceFunctionsTest(unittest.TestCase): - """Tests for letsencrypt.plugins.common.*_namespace functions.""" + """Tests for certbot.plugins.common.*_namespace functions.""" def test_option_namespace(self): - from letsencrypt.plugins.common import option_namespace + from certbot.plugins.common import option_namespace self.assertEqual("foo-", option_namespace("foo")) def test_dest_namespace(self): - from letsencrypt.plugins.common import dest_namespace + from certbot.plugins.common import dest_namespace self.assertEqual("foo_", dest_namespace("foo")) def test_dest_namespace_with_dashes(self): - from letsencrypt.plugins.common import dest_namespace + from certbot.plugins.common import dest_namespace self.assertEqual("foo_bar_", dest_namespace("foo-bar")) class PluginTest(unittest.TestCase): - """Test for letsencrypt.plugins.common.Plugin.""" + """Test for certbot.plugins.common.Plugin.""" def setUp(self): - from letsencrypt.plugins.common import Plugin + from certbot.plugins.common import Plugin class MockPlugin(Plugin): # pylint: disable=missing-docstring @classmethod @@ -74,10 +74,10 @@ class PluginTest(unittest.TestCase): class AddrTest(unittest.TestCase): - """Tests for letsencrypt.client.plugins.common.Addr.""" + """Tests for certbot.client.plugins.common.Addr.""" def setUp(self): - from letsencrypt.plugins.common import Addr + from certbot.plugins.common import Addr self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:*") self.addr3 = Addr.fromstring("192.168.1.1:80") @@ -132,13 +132,13 @@ class AddrTest(unittest.TestCase): self.assertEqual(self.addr4, self.addr4.get_addr_obj("")) self.assertNotEqual(self.addr4, self.addr5) self.assertFalse(self.addr4 == 3333) - from letsencrypt.plugins.common import Addr + from certbot.plugins.common import Addr self.assertEqual(self.addr4, Addr.fromstring("[fe00:0:0::1]")) self.assertEqual(self.addr4, Addr.fromstring("[fe00:0::0:0:1]")) def test_set_inclusion(self): - from letsencrypt.plugins.common import Addr + from certbot.plugins.common import Addr set_a = set([self.addr1, self.addr2]) addr1b = Addr.fromstring("192.168.1.1") addr2b = Addr.fromstring("192.168.1.1:*") @@ -155,7 +155,7 @@ class AddrTest(unittest.TestCase): class TLSSNI01Test(unittest.TestCase): - """Tests for letsencrypt.plugins.common.TLSSNI01.""" + """Tests for certbot.plugins.common.TLSSNI01.""" auth_key = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) achalls = [ @@ -166,11 +166,11 @@ class TLSSNI01Test(unittest.TestCase): achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.chall_to_challb( challenges.TLSSNI01(token=b'token2'), "pending"), - domain="letsencrypt.demo", account_key=auth_key), + domain="certbot.demo", account_key=auth_key), ] def setUp(self): - from letsencrypt.plugins.common import TLSSNI01 + from certbot.plugins.common import TLSSNI01 self.sni = TLSSNI01(configurator=mock.MagicMock()) def test_add_chall(self): @@ -191,9 +191,9 @@ class TLSSNI01Test(unittest.TestCase): achall.response_and_validation.return_value = ( response, (test_util.load_cert("cert.pem"), key)) - with mock.patch("letsencrypt.plugins.common.open", + with mock.patch("certbot.plugins.common.open", mock_open, create=True): - with mock.patch("letsencrypt.plugins.common.le_util.safe_open", + with mock.patch("certbot.plugins.common.le_util.safe_open", mock_safe_open): # pylint: disable=protected-access self.assertEqual(response, self.sni._setup_challenge_cert( diff --git a/letsencrypt/plugins/disco.py b/certbot/plugins/disco.py similarity index 97% rename from letsencrypt/plugins/disco.py rename to certbot/plugins/disco.py index 27d2fb541..eb3851d34 100644 --- a/letsencrypt/plugins/disco.py +++ b/certbot/plugins/disco.py @@ -6,9 +6,9 @@ import pkg_resources import zope.interface import zope.interface.verify -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import constants +from certbot import errors +from certbot import interfaces logger = logging.getLogger(__name__) @@ -18,9 +18,9 @@ class PluginEntryPoint(object): """Plugin entry point.""" PREFIX_FREE_DISTRIBUTIONS = [ - "letsencrypt", - "letsencrypt-apache", - "letsencrypt-nginx", + "certbot", + "certbot-apache", + "certbot-nginx", ] """Distributions for which prefix will be omitted.""" diff --git a/letsencrypt/plugins/disco_test.py b/certbot/plugins/disco_test.py similarity index 92% rename from letsencrypt/plugins/disco_test.py rename to certbot/plugins/disco_test.py index 1aeaf81c1..086980695 100644 --- a/letsencrypt/plugins/disco_test.py +++ b/certbot/plugins/disco_test.py @@ -1,23 +1,23 @@ -"""Tests for letsencrypt.plugins.disco.""" +"""Tests for certbot.plugins.disco.""" import unittest import mock import pkg_resources import zope.interface -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -from letsencrypt.plugins import standalone +from certbot.plugins import standalone EP_SA = pkg_resources.EntryPoint( - "sa", "letsencrypt.plugins.standalone", + "sa", "certbot.plugins.standalone", attrs=("Authenticator",), - dist=mock.MagicMock(key="letsencrypt")) + dist=mock.MagicMock(key="certbot")) class PluginEntryPointTest(unittest.TestCase): - """Tests for letsencrypt.plugins.disco.PluginEntryPoint.""" + """Tests for certbot.plugins.disco.PluginEntryPoint.""" def setUp(self): self.ep1 = pkg_resources.EntryPoint( @@ -31,11 +31,11 @@ class PluginEntryPointTest(unittest.TestCase): self.ep3 = pkg_resources.EntryPoint( "ep3", "a.ep3", dist=mock.MagicMock(key="p3")) - from letsencrypt.plugins.disco import PluginEntryPoint + from certbot.plugins.disco import PluginEntryPoint self.plugin_ep = PluginEntryPoint(EP_SA) def test_entry_point_to_plugin_name(self): - from letsencrypt.plugins.disco import PluginEntryPoint + from certbot.plugins.disco import PluginEntryPoint names = { self.ep1: "p1:ep1", @@ -100,7 +100,7 @@ class PluginEntryPointTest(unittest.TestCase): self.plugin_ep._initialized = plugin = mock.MagicMock() exceptions = zope.interface.exceptions - with mock.patch("letsencrypt.plugins." + with mock.patch("certbot.plugins." "disco.zope.interface") as mock_zope: mock_zope.exceptions = exceptions @@ -164,18 +164,18 @@ class PluginEntryPointTest(unittest.TestCase): class PluginsRegistryTest(unittest.TestCase): - """Tests for letsencrypt.plugins.disco.PluginsRegistry.""" + """Tests for certbot.plugins.disco.PluginsRegistry.""" def setUp(self): - from letsencrypt.plugins.disco import PluginsRegistry + from certbot.plugins.disco import PluginsRegistry self.plugin_ep = mock.MagicMock(name="mock") self.plugin_ep.__hash__.side_effect = TypeError self.plugins = {"mock": self.plugin_ep} self.reg = PluginsRegistry(self.plugins) def test_find_all(self): - from letsencrypt.plugins.disco import PluginsRegistry - with mock.patch("letsencrypt.plugins.disco.pkg_resources") as mock_pkg: + from certbot.plugins.disco import PluginsRegistry + with mock.patch("certbot.plugins.disco.pkg_resources") as mock_pkg: mock_pkg.iter_entry_points.return_value = iter([EP_SA]) plugins = PluginsRegistry.find_all() self.assertTrue(plugins["sa"].plugin_cls is standalone.Authenticator) diff --git a/letsencrypt/plugins/manual.py b/certbot/plugins/manual.py similarity index 96% rename from letsencrypt/plugins/manual.py rename to certbot/plugins/manual.py index 47c8ff6e4..9b722aef4 100644 --- a/letsencrypt/plugins/manual.py +++ b/certbot/plugins/manual.py @@ -15,9 +15,9 @@ import zope.interface from acme import challenges -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.plugins import common +from certbot import errors +from certbot import interfaces +from certbot.plugins import common logger = logging.getLogger(__name__) @@ -55,13 +55,13 @@ command on the target server (as root): # 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, \ +If you're running certbot 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, + # "cd /tmp/certbot" makes sure user doesn't serve /root, # separate "public_html" ensures that cert.pem/key.pem are not # served and makes it more obvious that Python command will serve # anything recursively under the cwd @@ -80,7 +80,7 @@ s.serve_forever()" """ def __init__(self, *args, **kwargs): super(Authenticator, self).__init__(*args, **kwargs) self._root = (tempfile.mkdtemp() if self.conf("test-mode") - else "/tmp/letsencrypt") + else "/tmp/certbot") self._httpd = None @classmethod diff --git a/letsencrypt/plugins/manual_test.py b/certbot/plugins/manual_test.py similarity index 78% rename from letsencrypt/plugins/manual_test.py rename to certbot/plugins/manual_test.py index e749eb1f9..af1dc9909 100644 --- a/letsencrypt/plugins/manual_test.py +++ b/certbot/plugins/manual_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.manual.""" +"""Tests for certbot.plugins.manual.""" import signal import unittest @@ -7,21 +7,21 @@ import mock from acme import challenges from acme import jose -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class AuthenticatorTest(unittest.TestCase): - """Tests for letsencrypt.plugins.manual.Authenticator.""" + """Tests for certbot.plugins.manual.Authenticator.""" def setUp(self): - from letsencrypt.plugins.manual import Authenticator + from certbot.plugins.manual import Authenticator self.config = mock.MagicMock( http01_port=8080, manual_test_mode=False, manual_public_ip_logging_ok=False, noninteractive_mode=True) @@ -48,8 +48,8 @@ class AuthenticatorTest(unittest.TestCase): def test_perform_empty(self): self.assertEqual([], self.auth.perform([])) - @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") - @mock.patch("letsencrypt.plugins.manual.sys.stdout") + @mock.patch("certbot.plugins.manual.zope.component.getUtility") + @mock.patch("certbot.plugins.manual.sys.stdout") @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): @@ -66,12 +66,12 @@ class AuthenticatorTest(unittest.TestCase): self.assertTrue(self.achalls[0].chall.encode("token") in message) mock_verify.return_value = False - with mock.patch("letsencrypt.plugins.manual.logger") as mock_logger: + with mock.patch("certbot.plugins.manual.logger") as mock_logger: self.auth.perform(self.achalls) mock_logger.warning.assert_called_once_with(mock.ANY) - @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") - @mock.patch("letsencrypt.plugins.manual.Authenticator._notify_and_wait") + @mock.patch("certbot.plugins.manual.zope.component.getUtility") + @mock.patch("certbot.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, \ @@ -79,14 +79,14 @@ class AuthenticatorTest(unittest.TestCase): self.assertRaises(errors.PluginError, self.auth.perform, self.achalls) - @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) + @mock.patch("certbot.plugins.manual.subprocess.Popen", autospec=True) def test_perform_test_command_oserror(self, mock_popen): mock_popen.side_effect = OSError self.assertEqual([False], self.auth_test_mode.perform(self.achalls)) - @mock.patch("letsencrypt.plugins.manual.socket.socket") - @mock.patch("letsencrypt.plugins.manual.time.sleep", autospec=True) - @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) + @mock.patch("certbot.plugins.manual.socket.socket") + @mock.patch("certbot.plugins.manual.time.sleep", autospec=True) + @mock.patch("certbot.plugins.manual.subprocess.Popen", autospec=True) def test_perform_test_command_run_failure( self, mock_popen, unused_mock_sleep, unused_mock_socket): mock_popen.poll.return_value = 10 @@ -100,7 +100,7 @@ class AuthenticatorTest(unittest.TestCase): httpd.poll.return_value = 0 self.auth_test_mode.cleanup(self.achalls) - @mock.patch("letsencrypt.plugins.manual.os.killpg", autospec=True) + @mock.patch("certbot.plugins.manual.os.killpg", autospec=True) def test_cleanup_test_mode_kills_still_running(self, mock_killpg): # pylint: disable=protected-access self.auth_test_mode._httpd = httpd = mock.Mock(pid=1234) diff --git a/letsencrypt/plugins/null.py b/certbot/plugins/null.py similarity index 94% rename from letsencrypt/plugins/null.py rename to certbot/plugins/null.py index 2c643d495..995b96274 100644 --- a/letsencrypt/plugins/null.py +++ b/certbot/plugins/null.py @@ -4,8 +4,8 @@ import logging import zope.component import zope.interface -from letsencrypt import interfaces -from letsencrypt.plugins import common +from certbot import interfaces +from certbot.plugins import common logger = logging.getLogger(__name__) diff --git a/letsencrypt/plugins/null_test.py b/certbot/plugins/null_test.py similarity index 77% rename from letsencrypt/plugins/null_test.py rename to certbot/plugins/null_test.py index 008bb0381..305954a2f 100644 --- a/letsencrypt/plugins/null_test.py +++ b/certbot/plugins/null_test.py @@ -1,14 +1,14 @@ -"""Tests for letsencrypt.plugins.null.""" +"""Tests for certbot.plugins.null.""" import unittest import mock class InstallerTest(unittest.TestCase): - """Tests for letsencrypt.plugins.null.Installer.""" + """Tests for certbot.plugins.null.Installer.""" def setUp(self): - from letsencrypt.plugins.null import Installer + from certbot.plugins.null import Installer self.installer = Installer(config=mock.MagicMock(), name="null") def test_it(self): diff --git a/letsencrypt/plugins/selection.py b/certbot/plugins/selection.py similarity index 96% rename from letsencrypt/plugins/selection.py rename to certbot/plugins/selection.py index 20f6ac512..0febeb82c 100644 --- a/letsencrypt/plugins/selection.py +++ b/certbot/plugins/selection.py @@ -6,10 +6,10 @@ import logging import zope.component -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -from letsencrypt.display import util as display_util +from certbot.display import util as display_util logger = logging.getLogger(__name__) z_util = zope.component.getUtility @@ -42,9 +42,9 @@ def pick_authenticator( def pick_plugin(config, default, plugins, question, ifaces): """Pick plugin. - :param letsencrypt.interfaces.IConfig: Configuration + :param certbot.interfaces.IConfig: Configuration :param str default: Plugin name supplied by user or ``None``. - :param letsencrypt.plugins.disco.PluginsRegistry plugins: + :param certbot.plugins.disco.PluginsRegistry plugins: All plugins registered as entry points. :param str question: Question to be presented to the user in case multiple candidates are found. @@ -158,7 +158,7 @@ def choose_configurator_plugins(config, plugins, verb): # Which plugins do we need? if verb == "run": need_inst = need_auth = True - from letsencrypt.cli import cli_command + from certbot.cli import cli_command if req_auth in noninstaller_plugins and not req_inst: msg = ('With the {0} plugin, you probably want to use the "certonly" command, eg:{1}' '{1} {2} certonly --{0}{1}{1}' @@ -263,7 +263,7 @@ 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 "certonly" command') + 'fix that or try running certbot 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 ' diff --git a/letsencrypt/plugins/selection_test.py b/certbot/plugins/selection_test.py similarity index 80% rename from letsencrypt/plugins/selection_test.py rename to certbot/plugins/selection_test.py index 0beaab076..001ca5cff 100644 --- a/letsencrypt/plugins/selection_test.py +++ b/certbot/plugins/selection_test.py @@ -5,40 +5,40 @@ import unittest import mock import zope.component -from letsencrypt.display import util as display_util -from letsencrypt import interfaces +from certbot.display import util as display_util +from certbot import interfaces class ConveniencePickPluginTest(unittest.TestCase): - """Tests for letsencrypt.plugins.selection.pick_*.""" + """Tests for certbot.plugins.selection.pick_*.""" def _test(self, fun, ifaces): config = mock.Mock() default = mock.Mock() plugins = mock.Mock() - with mock.patch("letsencrypt.plugins.selection.pick_plugin") as mock_p: + with mock.patch("certbot.plugins.selection.pick_plugin") as mock_p: mock_p.return_value = "foo" self.assertEqual("foo", fun(config, default, plugins, "Question?")) mock_p.assert_called_once_with( config, default, plugins, "Question?", ifaces) def test_authenticator(self): - from letsencrypt.plugins.selection import pick_authenticator + from certbot.plugins.selection import pick_authenticator self._test(pick_authenticator, (interfaces.IAuthenticator,)) def test_installer(self): - from letsencrypt.plugins.selection import pick_installer + from certbot.plugins.selection import pick_installer self._test(pick_installer, (interfaces.IInstaller,)) def test_configurator(self): - from letsencrypt.plugins.selection import pick_configurator + from certbot.plugins.selection import pick_configurator self._test(pick_configurator, (interfaces.IAuthenticator, interfaces.IInstaller)) class PickPluginTest(unittest.TestCase): - """Tests for letsencrypt.plugins.selection.pick_plugin.""" + """Tests for certbot.plugins.selection.pick_plugin.""" def setUp(self): self.config = mock.Mock(noninteractive_mode=False) @@ -48,7 +48,7 @@ class PickPluginTest(unittest.TestCase): self.ifaces = [] def _call(self): - from letsencrypt.plugins.selection import pick_plugin + from certbot.plugins.selection import pick_plugin return pick_plugin(self.config, self.default, self.reg, self.question, self.ifaces) @@ -89,7 +89,7 @@ class PickPluginTest(unittest.TestCase): "bar": plugin_ep, "baz": plugin_ep, } - with mock.patch("letsencrypt.plugins.selection.choose_plugin") as mock_choose: + with mock.patch("certbot.plugins.selection.choose_plugin") as mock_choose: mock_choose.return_value = plugin_ep self.assertEqual("foo", self._call()) mock_choose.assert_called_once_with( @@ -101,13 +101,13 @@ class PickPluginTest(unittest.TestCase): "baz": None, } - with mock.patch("letsencrypt.plugins.selection.choose_plugin") as mock_choose: + with mock.patch("certbot.plugins.selection.choose_plugin") as mock_choose: mock_choose.return_value = None self.assertTrue(self._call() is None) class ChoosePluginTest(unittest.TestCase): - """Tests for letsencrypt.plugins.selection.choose_plugin.""" + """Tests for certbot.plugins.selection.choose_plugin.""" def setUp(self): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -122,17 +122,17 @@ class ChoosePluginTest(unittest.TestCase): ] def _call(self): - from letsencrypt.plugins.selection import choose_plugin + from certbot.plugins.selection import choose_plugin return choose_plugin(self.plugins, "Question?") - @mock.patch("letsencrypt.plugins.selection.z_util") + @mock.patch("certbot.plugins.selection.z_util") def test_selection(self, mock_util): mock_util().menu.side_effect = [(display_util.OK, 0), (display_util.OK, 1)] self.assertEqual(self.mock_stand, self._call()) self.assertEqual(mock_util().notification.call_count, 1) - @mock.patch("letsencrypt.plugins.selection.z_util") + @mock.patch("certbot.plugins.selection.z_util") def test_more_info(self, mock_util): mock_util().menu.side_effect = [ (display_util.HELP, 0), @@ -143,7 +143,7 @@ class ChoosePluginTest(unittest.TestCase): self.assertEqual(self.mock_stand, self._call()) self.assertEqual(mock_util().notification.call_count, 2) - @mock.patch("letsencrypt.plugins.selection.z_util") + @mock.patch("certbot.plugins.selection.z_util") def test_no_choice(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 0) self.assertTrue(self._call() is None) diff --git a/letsencrypt/plugins/standalone.py b/certbot/plugins/standalone.py similarity index 97% rename from letsencrypt/plugins/standalone.py rename to certbot/plugins/standalone.py index acc253bca..a3bb1d8f0 100644 --- a/letsencrypt/plugins/standalone.py +++ b/certbot/plugins/standalone.py @@ -12,11 +12,11 @@ import zope.interface from acme import challenges from acme import standalone as acme_standalone -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -from letsencrypt.plugins import common -from letsencrypt.plugins import util +from certbot.plugins import common +from certbot.plugins import util logger = logging.getLogger(__name__) @@ -91,7 +91,7 @@ class ServerManager(object): *instance.server.socket.getsockname()[:2]) instance.server.shutdown() # Not calling server_close causes problems when renewing multiple - # certs with `letsencrypt renew` using TLSSNI01 and PyOpenSSL 0.13 + # certs with `certbot renew` using TLSSNI01 and PyOpenSSL 0.13 instance.server.server_close() instance.thread.join() del self._instances[port] diff --git a/letsencrypt/plugins/standalone_test.py b/certbot/plugins/standalone_test.py similarity index 91% rename from letsencrypt/plugins/standalone_test.py rename to certbot/plugins/standalone_test.py index 80f9c8a74..9f5b14591 100644 --- a/letsencrypt/plugins/standalone_test.py +++ b/certbot/plugins/standalone_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.standalone.""" +"""Tests for certbot.plugins.standalone.""" import argparse import socket import unittest @@ -10,19 +10,19 @@ from acme import challenges from acme import jose from acme import standalone as acme_standalone -from letsencrypt import achallenges -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import achallenges +from certbot import errors +from certbot import interfaces -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util class ServerManagerTest(unittest.TestCase): - """Tests for letsencrypt.plugins.standalone.ServerManager.""" + """Tests for certbot.plugins.standalone.ServerManager.""" def setUp(self): - from letsencrypt.plugins.standalone import ServerManager + from certbot.plugins.standalone import ServerManager self.certs = {} self.http_01_resources = {} self.mgr = ServerManager(self.certs, self.http_01_resources) @@ -68,7 +68,7 @@ class SupportedChallengesValidatorTest(unittest.TestCase): """Tests for plugins.standalone.supported_challenges_validator.""" def _call(self, data): - from letsencrypt.plugins.standalone import ( + from certbot.plugins.standalone import ( supported_challenges_validator) return supported_challenges_validator(data) @@ -87,10 +87,10 @@ class SupportedChallengesValidatorTest(unittest.TestCase): class AuthenticatorTest(unittest.TestCase): - """Tests for letsencrypt.plugins.standalone.Authenticator.""" + """Tests for certbot.plugins.standalone.Authenticator.""" def setUp(self): - from letsencrypt.plugins.standalone import Authenticator + from certbot.plugins.standalone import Authenticator self.config = mock.MagicMock( tls_sni_01_port=1234, http01_port=4321, standalone_supported_challenges="tls-sni-01,http-01") @@ -117,7 +117,7 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(self.auth.get_chall_pref(domain=None), [challenges.TLSSNI01]) - @mock.patch("letsencrypt.plugins.standalone.util") + @mock.patch("certbot.plugins.standalone.util") def test_perform_already_listening(self, mock_util): for chall, port in ((challenges.TLSSNI01.typ, 1234), (challenges.HTTP01.typ, 4321)): @@ -128,14 +128,14 @@ class AuthenticatorTest(unittest.TestCase): mock_util.already_listening.assert_called_once_with(port, False) mock_util.already_listening.reset_mock() - @mock.patch("letsencrypt.plugins.standalone.zope.component.getUtility") + @mock.patch("certbot.plugins.standalone.zope.component.getUtility") def test_perform(self, unused_mock_get_utility): achalls = [1, 2, 3] self.auth.perform2 = mock.Mock(return_value=mock.sentinel.responses) self.assertEqual(mock.sentinel.responses, self.auth.perform(achalls)) self.auth.perform2.assert_called_once_with(achalls) - @mock.patch("letsencrypt.plugins.standalone.zope.component.getUtility") + @mock.patch("certbot.plugins.standalone.zope.component.getUtility") def _test_perform_bind_errors(self, errno, achalls, mock_get_utility): def _perform2(unused_achalls): raise errors.StandaloneBindError(mock.Mock(errno=errno), 1234) diff --git a/letsencrypt/plugins/util.py b/certbot/plugins/util.py similarity index 98% rename from letsencrypt/plugins/util.py rename to certbot/plugins/util.py index 3382b73dd..5fc98dff6 100644 --- a/letsencrypt/plugins/util.py +++ b/certbot/plugins/util.py @@ -5,7 +5,7 @@ import socket import psutil import zope.component -from letsencrypt import interfaces +from certbot import interfaces logger = logging.getLogger(__name__) diff --git a/letsencrypt/plugins/util_test.py b/certbot/plugins/util_test.py similarity index 81% rename from letsencrypt/plugins/util_test.py rename to certbot/plugins/util_test.py index 1591976b0..9bc8793c7 100644 --- a/letsencrypt/plugins/util_test.py +++ b/certbot/plugins/util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.util.""" +"""Tests for certbot.plugins.util.""" import unittest import mock @@ -6,14 +6,14 @@ import psutil class AlreadyListeningTest(unittest.TestCase): - """Tests for letsencrypt.plugins.already_listening.""" + """Tests for certbot.plugins.already_listening.""" def _call(self, *args, **kwargs): - from letsencrypt.plugins.util import already_listening + from certbot.plugins.util import already_listening return already_listening(*args, **kwargs) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_race_condition(self, mock_get_utility, mock_process, mock_net): # This tests a race condition, or permission problem, or OS # incompatibility in which, for some reason, no process name can be @@ -36,9 +36,9 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.generic_notification.call_count, 0) mock_process.assert_called_once_with(4416) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_not_listening(self, mock_get_utility, mock_process, mock_net): from psutil._common import sconn conns = [ @@ -54,9 +54,9 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.generic_notification.call_count, 0) self.assertEqual(mock_process.call_count, 0) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_listening_ipv4(self, mock_get_utility, mock_process, mock_net): from psutil._common import sconn conns = [ @@ -75,9 +75,9 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.call_count, 1) mock_process.assert_called_once_with(4416) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") - @mock.patch("letsencrypt.plugins.util.psutil.Process") - @mock.patch("letsencrypt.plugins.util.zope.component.getUtility") + @mock.patch("certbot.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.Process") + @mock.patch("certbot.plugins.util.zope.component.getUtility") def test_listening_ipv6(self, mock_get_utility, mock_process, mock_net): from psutil._common import sconn conns = [ @@ -98,7 +98,7 @@ class AlreadyListeningTest(unittest.TestCase): self.assertEqual(mock_get_utility.call_count, 1) mock_process.assert_called_once_with(4420) - @mock.patch("letsencrypt.plugins.util.psutil.net_connections") + @mock.patch("certbot.plugins.util.psutil.net_connections") def test_access_denied_exception(self, mock_net): mock_net.side_effect = psutil.AccessDenied("") self.assertFalse(self._call(12345)) diff --git a/letsencrypt/plugins/webroot.py b/certbot/plugins/webroot.py similarity index 98% rename from letsencrypt/plugins/webroot.py rename to certbot/plugins/webroot.py index a0f7ef9c3..fbe703f40 100644 --- a/letsencrypt/plugins/webroot.py +++ b/certbot/plugins/webroot.py @@ -12,11 +12,11 @@ import zope.interface from acme import challenges -from letsencrypt import cli -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.display import util as display_util -from letsencrypt.plugins import common +from certbot import cli +from certbot import errors +from certbot import interfaces +from certbot.display import util as display_util +from certbot.plugins import common logger = logging.getLogger(__name__) diff --git a/letsencrypt/plugins/webroot_test.py b/certbot/plugins/webroot_test.py similarity index 92% rename from letsencrypt/plugins/webroot_test.py rename to certbot/plugins/webroot_test.py index f7ed7fdbf..3f429ec34 100644 --- a/letsencrypt/plugins/webroot_test.py +++ b/certbot/plugins/webroot_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.plugins.webroot.""" +"""Tests for certbot.plugins.webroot.""" from __future__ import print_function @@ -16,25 +16,25 @@ import six from acme import challenges from acme import jose -from letsencrypt import achallenges -from letsencrypt import errors -from letsencrypt.display import util as display_util +from certbot import achallenges +from certbot import errors +from certbot.display import util as display_util -from letsencrypt.tests import acme_util -from letsencrypt.tests import test_util +from certbot.tests import acme_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class AuthenticatorTest(unittest.TestCase): - """Tests for letsencrypt.plugins.webroot.Authenticator.""" + """Tests for certbot.plugins.webroot.Authenticator.""" achall = achallenges.KeyAuthorizationAnnotatedChallenge( challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): - from letsencrypt.plugins.webroot import Authenticator + from certbot.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.root_challenge_path = os.path.join( self.path, ".well-known", "acme-challenge") @@ -61,7 +61,7 @@ class AuthenticatorTest(unittest.TestCase): def test_prepare(self): self.auth.prepare() # shouldn't raise any exceptions - @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") + @mock.patch("certbot.plugins.webroot.zope.component.getUtility") def test_webroot_from_list(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"otherthing.com": self.path} @@ -78,7 +78,7 @@ class AuthenticatorTest(unittest.TestCase): self.assertEqual(self.config.webroot_map[self.achall.domain], self.path) - @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") + @mock.patch("certbot.plugins.webroot.zope.component.getUtility") def test_webroot_from_list_help_and_cancel(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {"otherthing.com": self.path} @@ -95,7 +95,7 @@ class AuthenticatorTest(unittest.TestCase): webroot in call[0][1] for webroot in six.itervalues(self.config.webroot_map))) - @mock.patch("letsencrypt.plugins.webroot.zope.component.getUtility") + @mock.patch("certbot.plugins.webroot.zope.component.getUtility") def test_new_webroot(self, mock_get_utility): self.config.webroot_path = [] self.config.webroot_map = {} @@ -137,12 +137,12 @@ class AuthenticatorTest(unittest.TestCase): self.assertRaises(errors.PluginError, self.auth.perform, []) os.chmod(self.path, 0o700) - @mock.patch("letsencrypt.plugins.webroot.os.chown") + @mock.patch("certbot.plugins.webroot.os.chown") def test_failed_chown_eacces(self, mock_chown): mock_chown.side_effect = OSError(errno.EACCES, "msg") self.auth.perform([self.achall]) # exception caught and logged - @mock.patch("letsencrypt.plugins.webroot.os.chown") + @mock.patch("certbot.plugins.webroot.os.chown") def test_failed_chown_not_eacces(self, mock_chown): mock_chown.side_effect = OSError() self.assertRaises(errors.PluginError, self.auth.perform, []) @@ -233,7 +233,7 @@ class WebrootActionTest(unittest.TestCase): challb=acme_util.HTTP01_P, domain="thing.com", account_key=KEY) def setUp(self): - from letsencrypt.plugins.webroot import Authenticator + from certbot.plugins.webroot import Authenticator self.path = tempfile.mkdtemp() self.parser = argparse.ArgumentParser() self.parser.add_argument("-d", "--domains", @@ -266,7 +266,7 @@ class WebrootActionTest(unittest.TestCase): config.webroot_map[self.achall.domain], self.path) def _get_config_after_perform(self, config): - from letsencrypt.plugins.webroot import Authenticator + from certbot.plugins.webroot import Authenticator auth = Authenticator(config, "webroot") auth.perform([self.achall]) return auth.config diff --git a/letsencrypt/renewal.py b/certbot/renewal.py similarity index 96% rename from letsencrypt/renewal.py rename to certbot/renewal.py index badbdeb0e..180499387 100644 --- a/letsencrypt/renewal.py +++ b/certbot/renewal.py @@ -11,17 +11,17 @@ import zope.component import OpenSSL -from letsencrypt import configuration -from letsencrypt import cli -from letsencrypt import constants +from certbot import configuration +from certbot import cli +from certbot import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import hooks -from letsencrypt import storage -from letsencrypt.plugins import disco as plugins_disco +from certbot import crypto_util +from certbot import errors +from certbot import interfaces +from certbot import le_util +from certbot import hooks +from certbot import storage +from certbot.plugins import disco as plugins_disco logger = logging.getLogger(__name__) @@ -262,7 +262,7 @@ def _renew_describe_results(config, renew_successes, renew_failures, notify = out.append if config.dry_run: - notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + notify("** DRY RUN: simulating 'certbot renew' close to cert expiry") notify("** (The test certificates below have not been saved.)") notify("") if renew_skipped: @@ -290,7 +290,7 @@ def _renew_describe_results(config, renew_successes, renew_failures, notify(parse_failures, "parsefail") if config.dry_run: - notify("** DRY RUN: simulating 'letsencrypt renew' close to cert expiry") + notify("** DRY RUN: simulating 'certbot renew' close to cert expiry") notify("** (The test certificates above have not been saved.)") if config.quiet and not (renew_failures or parse_failures): @@ -338,7 +338,7 @@ def renew_all_lineages(config): zope.component.provideUtility(lineage_config) if should_renew(lineage_config, renewal_candidate): plugins = plugins_disco.PluginsRegistry.find_all() - from letsencrypt import main + from certbot import main main.obtain_cert(lineage_config, plugins, renewal_candidate) renew_successes.append(renewal_candidate.fullchain) else: diff --git a/letsencrypt/reporter.py b/certbot/reporter.py similarity index 98% rename from letsencrypt/reporter.py rename to certbot/reporter.py index f3ab93763..d509cb0b8 100644 --- a/letsencrypt/reporter.py +++ b/certbot/reporter.py @@ -10,8 +10,8 @@ import textwrap from six.moves import queue # pylint: disable=import-error import zope.interface -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import interfaces +from certbot import le_util logger = logging.getLogger(__name__) diff --git a/letsencrypt/reverter.py b/certbot/reverter.py similarity index 97% rename from letsencrypt/reverter.py rename to certbot/reverter.py index 80bc76174..6017ef602 100644 --- a/letsencrypt/reverter.py +++ b/certbot/reverter.py @@ -9,12 +9,12 @@ import traceback import zope.component -from letsencrypt import constants -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import constants +from certbot import errors +from certbot import interfaces +from certbot import le_util -from letsencrypt.display import util as display_util +from certbot.display import util as display_util logger = logging.getLogger(__name__) @@ -26,7 +26,7 @@ class Reverter(object): .. note:: Consider moving everything over to CSV format. :param config: Configuration. - :type config: :class:`letsencrypt.interfaces.IConfig` + :type config: :class:`certbot.interfaces.IConfig` """ def __init__(self, config): @@ -100,7 +100,7 @@ class Reverter(object): """Displays all saved checkpoints. All checkpoints are printed by - :meth:`letsencrypt.interfaces.IDisplay.notification`. + :meth:`certbot.interfaces.IDisplay.notification`. .. todo:: Decide on a policy for error handling, OSError IOError... @@ -291,7 +291,7 @@ class Reverter(object): :param set save_files: Set of files about to be saved. - :raises letsencrypt.errors.ReverterError: + :raises certbot.errors.ReverterError: when save is attempting to overwrite a temporary file. """ @@ -317,7 +317,7 @@ class Reverter(object): "file - %s" % filename) def register_file_creation(self, temporary, *files): - r"""Register the creation of all files during letsencrypt execution. + r"""Register the creation of all files during certbot execution. Call this method before writing to the file to make sure that the file will be cleaned up if the program exits unexpectedly. @@ -327,7 +327,7 @@ class Reverter(object): a temp or permanent save. :param \*files: file paths (str) to be registered - :raises letsencrypt.errors.ReverterError: If + :raises certbot.errors.ReverterError: If call does not contain necessary parameters or if the file creation is unable to be registered. @@ -439,7 +439,7 @@ class Reverter(object): :returns: Success :rtype: bool - :raises letsencrypt.errors.ReverterError: If + :raises certbot.errors.ReverterError: If all files within file_list cannot be removed """ @@ -477,7 +477,7 @@ class Reverter(object): :param str title: Title describing checkpoint - :raises letsencrypt.errors.ReverterError: when the + :raises certbot.errors.ReverterError: when the checkpoint is not able to be finalized. """ diff --git a/letsencrypt/storage.py b/certbot/storage.py similarity index 99% rename from letsencrypt/storage.py rename to certbot/storage.py index 74655fb3a..1a6dee892 100644 --- a/letsencrypt/storage.py +++ b/certbot/storage.py @@ -8,11 +8,11 @@ import configobj import parsedatetime import pytz -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import error_handler -from letsencrypt import le_util +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import error_handler +from certbot import le_util logger = logging.getLogger(__name__) @@ -137,8 +137,8 @@ def _relevant(option): :rtype: bool """ # The list() here produces a list of the plugin names as strings. - from letsencrypt import renewal - from letsencrypt.plugins import disco as plugins_disco + from certbot import renewal + from certbot.plugins import disco as plugins_disco plugins = list(plugins_disco.PluginsRegistry.find_all()) return (option in renewal.STR_CONFIG_ITEMS or option in renewal.INT_CONFIG_ITEMS @@ -153,7 +153,7 @@ def relevant_values(all_values): :returns: A new dictionary containing items that can be used in renewal. :rtype dict:""" - from letsencrypt import cli + from certbot import cli def _is_cli_default(option, value): # Look through the CLI parser defaults and see if this option is diff --git a/letsencrypt/tests/__init__.py b/certbot/tests/__init__.py similarity index 100% rename from letsencrypt/tests/__init__.py rename to certbot/tests/__init__.py diff --git a/letsencrypt/tests/account_test.py b/certbot/tests/account_test.py similarity index 82% rename from letsencrypt/tests/account_test.py rename to certbot/tests/account_test.py index 9452a74f3..a96e57507 100644 --- a/letsencrypt/tests/account_test.py +++ b/certbot/tests/account_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.account.""" +"""Tests for certbot.account.""" import datetime import os import shutil @@ -12,29 +12,29 @@ import pytz from acme import jose from acme import messages -from letsencrypt import errors +from certbot import errors -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key_2.pem")) class AccountTest(unittest.TestCase): - """Tests for letsencrypt.account.Account.""" + """Tests for certbot.account.Account.""" def setUp(self): - from letsencrypt.account import Account + from certbot.account import Account self.regr = mock.MagicMock() self.meta = Account.Meta( - creation_host="test.letsencrypt.org", + creation_host="test.certbot.org", creation_dt=datetime.datetime( 2015, 7, 4, 14, 4, 10, tzinfo=pytz.UTC)) self.acc = Account(self.regr, KEY, self.meta) - with mock.patch("letsencrypt.account.socket") as mock_socket: - mock_socket.getfqdn.return_value = "test.letsencrypt.org" - with mock.patch("letsencrypt.account.datetime") as mock_dt: + with mock.patch("certbot.account.socket") as mock_socket: + mock_socket.getfqdn.return_value = "test.certbot.org" + with mock.patch("certbot.account.datetime") as mock_dt: mock_dt.datetime.now.return_value = self.meta.creation_dt self.acc_no_meta = Account(self.regr, KEY) @@ -49,7 +49,7 @@ class AccountTest(unittest.TestCase): def test_slug(self): self.assertEqual( - self.acc.slug, "test.letsencrypt.org@2015-07-04T14:04:10Z (bca5)") + self.acc.slug, "test.certbot.org@2015-07-04T14:04:10Z (bca5)") def test_repr(self): self.assertEqual( @@ -58,7 +58,7 @@ class AccountTest(unittest.TestCase): class ReportNewAccountTest(unittest.TestCase): - """Tests for letsencrypt.account.report_new_account.""" + """Tests for certbot.account.report_new_account.""" def setUp(self): self.config = mock.MagicMock(config_dir="/etc/letsencrypt") @@ -67,15 +67,15 @@ class ReportNewAccountTest(unittest.TestCase): uri=None, new_authzr_uri=None, body=reg)) def _call(self): - from letsencrypt.account import report_new_account + from certbot.account import report_new_account report_new_account(self.acc, self.config) - @mock.patch("letsencrypt.account.zope.component.queryUtility") + @mock.patch("certbot.account.zope.component.queryUtility") def test_no_reporter(self, mock_zope): mock_zope.return_value = None self._call() - @mock.patch("letsencrypt.account.zope.component.queryUtility") + @mock.patch("certbot.account.zope.component.queryUtility") def test_it(self, mock_zope): self._call() call_list = mock_zope().add_message.call_args_list @@ -85,10 +85,10 @@ class ReportNewAccountTest(unittest.TestCase): class AccountMemoryStorageTest(unittest.TestCase): - """Tests for letsencrypt.account.AccountMemoryStorage.""" + """Tests for certbot.account.AccountMemoryStorage.""" def setUp(self): - from letsencrypt.account import AccountMemoryStorage + from certbot.account import AccountMemoryStorage self.storage = AccountMemoryStorage() def test_it(self): @@ -103,16 +103,16 @@ class AccountMemoryStorageTest(unittest.TestCase): class AccountFileStorageTest(unittest.TestCase): - """Tests for letsencrypt.account.AccountFileStorage.""" + """Tests for certbot.account.AccountFileStorage.""" def setUp(self): self.tmp = tempfile.mkdtemp() self.config = mock.MagicMock( accounts_dir=os.path.join(self.tmp, "accounts")) - from letsencrypt.account import AccountFileStorage + from certbot.account import AccountFileStorage self.storage = AccountFileStorage(self.config) - from letsencrypt.account import Account + from certbot.account import Account self.acc = Account( regr=messages.RegistrationResource( uri=None, new_authzr_uri=None, body=messages.Registration()), @@ -151,7 +151,7 @@ class AccountFileStorageTest(unittest.TestCase): def test_find_all_load_skips(self): self.storage.load = mock.MagicMock( side_effect=["x", errors.AccountStorageError, "z"]) - with mock.patch("letsencrypt.account.os.listdir") as mock_listdir: + with mock.patch("certbot.account.os.listdir") as mock_listdir: mock_listdir.return_value = ["x", "y", "z"] self.assertEqual(["x", "z"], self.storage.find_all()) diff --git a/letsencrypt/tests/acme_util.py b/certbot/tests/acme_util.py similarity index 98% rename from letsencrypt/tests/acme_util.py rename to certbot/tests/acme_util.py index ea5438923..3d33c5723 100644 --- a/letsencrypt/tests/acme_util.py +++ b/certbot/tests/acme_util.py @@ -6,7 +6,7 @@ from acme import challenges from acme import jose from acme import messages -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = test_util.load_rsa_private_key('rsa512_key.pem') diff --git a/letsencrypt/tests/auth_handler_test.py b/certbot/tests/auth_handler_test.py similarity index 90% rename from letsencrypt/tests/auth_handler_test.py rename to certbot/tests/auth_handler_test.py index b7ac04984..3facd4f7c 100644 --- a/letsencrypt/tests/auth_handler_test.py +++ b/certbot/tests/auth_handler_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.auth_handler.""" +"""Tests for certbot.auth_handler.""" import functools import logging import unittest @@ -9,18 +9,18 @@ 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 +from certbot import achallenges +from certbot import errors +from certbot import le_util -from letsencrypt.tests import acme_util +from certbot.tests import acme_util class ChallengeFactoryTest(unittest.TestCase): # pylint: disable=protected-access def setUp(self): - from letsencrypt.auth_handler import AuthHandler + from certbot.auth_handler import AuthHandler # Account is mocked... self.handler = AuthHandler(None, None, mock.Mock(key="mock_key")) @@ -61,7 +61,7 @@ class GetAuthorizationsTest(unittest.TestCase): """ def setUp(self): - from letsencrypt.auth_handler import AuthHandler + from certbot.auth_handler import AuthHandler self.mock_auth = mock.MagicMock(name="ApacheConfigurator") @@ -80,7 +80,7 @@ class GetAuthorizationsTest(unittest.TestCase): def tearDown(self): logging.disable(logging.NOTSET) - @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") + @mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") def test_name1_tls_sni_01_1(self, mock_poll): self.mock_net.request_domain_challenges.side_effect = functools.partial( gen_dom_authzr, challs=acme_util.CHALLENGES) @@ -103,7 +103,7 @@ class GetAuthorizationsTest(unittest.TestCase): self.assertEqual(len(authzr), 1) - @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") + @mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") def test_name1_tls_sni_01_1_http_01_1_dns_1(self, mock_poll): self.mock_net.request_domain_challenges.side_effect = functools.partial( gen_dom_authzr, challs=acme_util.CHALLENGES, combos=False) @@ -129,7 +129,7 @@ class GetAuthorizationsTest(unittest.TestCase): # Length of authorizations list self.assertEqual(len(authzr), 1) - @mock.patch("letsencrypt.auth_handler.AuthHandler._poll_challenges") + @mock.patch("certbot.auth_handler.AuthHandler._poll_challenges") def test_name3_tls_sni_01_3(self, mock_poll): self.mock_net.request_domain_challenges.side_effect = functools.partial( gen_dom_authzr, challs=acme_util.CHALLENGES) @@ -182,8 +182,8 @@ class PollChallengesTest(unittest.TestCase): """Test poll challenges.""" def setUp(self): - from letsencrypt.auth_handler import challb_to_achall - from letsencrypt.auth_handler import AuthHandler + from certbot.auth_handler import challb_to_achall + from certbot.auth_handler import AuthHandler # Account and network are mocked... self.mock_net = mock.MagicMock() @@ -210,7 +210,7 @@ class PollChallengesTest(unittest.TestCase): challb_to_achall(challb, mock.Mock(key="dummy_key"), dom) for challb in self.handler.authzr[dom].body.challenges] - @mock.patch("letsencrypt.auth_handler.time") + @mock.patch("certbot.auth_handler.time") def test_poll_challenges(self, unused_mock_time): self.mock_net.poll.side_effect = self._mock_poll_solve_one_valid self.handler._poll_challenges(self.chall_update, False) @@ -218,7 +218,7 @@ class PollChallengesTest(unittest.TestCase): for authzr in self.handler.authzr.values(): self.assertEqual(authzr.body.status, messages.STATUS_VALID) - @mock.patch("letsencrypt.auth_handler.time") + @mock.patch("certbot.auth_handler.time") def test_poll_challenges_failure_best_effort(self, unused_mock_time): self.mock_net.poll.side_effect = self._mock_poll_solve_one_invalid self.handler._poll_challenges(self.chall_update, True) @@ -226,17 +226,17 @@ class PollChallengesTest(unittest.TestCase): for authzr in self.handler.authzr.values(): self.assertEqual(authzr.body.status, messages.STATUS_PENDING) - @mock.patch("letsencrypt.auth_handler.time") - @mock.patch("letsencrypt.auth_handler.zope.component.getUtility") + @mock.patch("certbot.auth_handler.time") + @mock.patch("certbot.auth_handler.zope.component.getUtility") def test_poll_challenges_failure(self, unused_mock_time, unused_mock_zope): self.mock_net.poll.side_effect = self._mock_poll_solve_one_invalid self.assertRaises( errors.AuthorizationError, self.handler._poll_challenges, self.chall_update, False) - @mock.patch("letsencrypt.auth_handler.time") + @mock.patch("certbot.auth_handler.time") def test_unable_to_find_challenge_status(self, unused_mock_time): - from letsencrypt.auth_handler import challb_to_achall + from certbot.auth_handler import challb_to_achall self.mock_net.poll.side_effect = self._mock_poll_solve_one_valid self.chall_update[self.doms[0]].append( challb_to_achall(acme_util.DNS_P, "key", self.doms[0])) @@ -295,10 +295,10 @@ class PollChallengesTest(unittest.TestCase): class ChallbToAchallTest(unittest.TestCase): - """Tests for letsencrypt.auth_handler.challb_to_achall.""" + """Tests for certbot.auth_handler.challb_to_achall.""" def _call(self, challb): - from letsencrypt.auth_handler import challb_to_achall + from certbot.auth_handler import challb_to_achall return challb_to_achall(challb, "account_key", "domain") def test_it(self): @@ -311,7 +311,7 @@ class ChallbToAchallTest(unittest.TestCase): class GenChallengePathTest(unittest.TestCase): - """Tests for letsencrypt.auth_handler.gen_challenge_path. + """Tests for certbot.auth_handler.gen_challenge_path. .. todo:: Add more tests for dumb_path... depending on what we want to do. @@ -324,7 +324,7 @@ class GenChallengePathTest(unittest.TestCase): @classmethod def _call(cls, challbs, preferences, combinations): - from letsencrypt.auth_handler import gen_challenge_path + from certbot.auth_handler import gen_challenge_path return gen_challenge_path(challbs, preferences, combinations) def test_common_case(self): @@ -354,7 +354,7 @@ class GenChallengePathTest(unittest.TestCase): class ReportFailedChallsTest(unittest.TestCase): - """Tests for letsencrypt.auth_handler._report_failed_challs.""" + """Tests for certbot.auth_handler._report_failed_challs.""" # pylint: disable=protected-access def setUp(self): @@ -388,18 +388,18 @@ class ReportFailedChallsTest(unittest.TestCase): domain="foo.bar", account_key="key") - @mock.patch("letsencrypt.auth_handler.zope.component.getUtility") + @mock.patch("certbot.auth_handler.zope.component.getUtility") def test_same_error_and_domain(self, mock_zope): - from letsencrypt import auth_handler + from certbot import auth_handler auth_handler._report_failed_challs([self.http01, self.tls_sni_same]) call_list = mock_zope().add_message.call_args_list self.assertTrue(len(call_list) == 1) self.assertTrue("Domain: example.com\nType: tls\nDetail: detail" in call_list[0][0][0]) - @mock.patch("letsencrypt.auth_handler.zope.component.getUtility") + @mock.patch("certbot.auth_handler.zope.component.getUtility") def test_different_errors_and_domains(self, mock_zope): - from letsencrypt import auth_handler + from certbot import auth_handler auth_handler._report_failed_challs([self.http01, self.tls_sni_diff]) self.assertTrue(mock_zope().add_message.call_count == 2) diff --git a/letsencrypt/tests/cli_test.py b/certbot/tests/cli_test.py similarity index 88% rename from letsencrypt/tests/cli_test.py rename to certbot/tests/cli_test.py index eb3f48308..31056cafe 100644 --- a/letsencrypt/tests/cli_test.py +++ b/certbot/tests/cli_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.cli.""" +"""Tests for certbot.cli.""" from __future__ import print_function import argparse @@ -16,22 +16,22 @@ from six.moves import reload_module # pylint: disable=import-error from acme import jose -from letsencrypt import account -from letsencrypt import cli -from letsencrypt import configuration -from letsencrypt import constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import le_util -from letsencrypt import main -from letsencrypt import renewal -from letsencrypt import storage +from certbot import account +from certbot import cli +from certbot import configuration +from certbot import constants +from certbot import crypto_util +from certbot import errors +from certbot import le_util +from certbot import main +from certbot import renewal +from certbot import storage -from letsencrypt.plugins import disco -from letsencrypt.plugins import manual +from certbot.plugins import disco +from certbot.plugins import manual -from letsencrypt.tests import storage_test -from letsencrypt.tests import test_util +from certbot.tests import storage_test +from certbot.tests import test_util CERT = test_util.vector_path('cert.pem') @@ -59,7 +59,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _call(self, args, stdout=None): "Run the cli with output streams and actual client mocked out" - with mock.patch('letsencrypt.main.client') as client: + with mock.patch('certbot.main.client') as client: ret, stdout, stderr = self._call_no_clientmock(args, stdout) return ret, stdout, stderr, client @@ -68,13 +68,13 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods args = self.standard_args + args toy_stdout = stdout if stdout else six.StringIO() - with mock.patch('letsencrypt.main.sys.stdout', new=toy_stdout): - with mock.patch('letsencrypt.main.sys.stderr') as stderr: + with mock.patch('certbot.main.sys.stdout', new=toy_stdout): + with mock.patch('certbot.main.sys.stderr') as stderr: ret = main.main(args[:]) # NOTE: parser can alter its args! return ret, toy_stdout, stderr def test_no_flags(self): - with mock.patch('letsencrypt.main.run') as mock_run: + with mock.patch('certbot.main.run') as mock_run: self._call([]) self.assertEqual(1, mock_run.call_count) @@ -133,7 +133,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "Ensure that a particular error raises a missing cli flag error containing message" exc = None try: - with mock.patch('letsencrypt.main.sys.stderr'): + with mock.patch('certbot.main.sys.stderr'): main.main(self.standard_args + args[:]) # NOTE: parser can alter its args! except errors.MissingCommandlineFlag as exc: self.assertTrue(message in str(exc)) @@ -144,15 +144,15 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._cli_missing_flag(args, "specify a plugin") args.extend(['--standalone', '-d', 'eg.is']) self._cli_missing_flag(args, "register before running") - with mock.patch('letsencrypt.main._auth_from_domains'): - with mock.patch('letsencrypt.main.client.acme_from_config_key'): + with mock.patch('certbot.main._auth_from_domains'): + with mock.patch('certbot.main.client.acme_from_config_key'): args.extend(['--email', 'io@io.is']) self._cli_missing_flag(args, "--agree-tos") - @mock.patch('letsencrypt.main.client.acme_client.Client') - @mock.patch('letsencrypt.main._determine_account') - @mock.patch('letsencrypt.main.client.Client.obtain_and_enroll_certificate') - @mock.patch('letsencrypt.main._auth_from_domains') + @mock.patch('certbot.main.client.acme_client.Client') + @mock.patch('certbot.main._determine_account') + @mock.patch('certbot.main.client.Client.obtain_and_enroll_certificate') + @mock.patch('certbot.main._auth_from_domains') def test_user_agent(self, afd, _obt, det, _client): # Normally the client is totally mocked out, but here we need more # arguments to automate it... @@ -161,7 +161,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods det.return_value = mock.MagicMock(), None afd.return_value = mock.MagicMock(), "newcert" - with mock.patch('letsencrypt.main.client.acme_client.ClientNetwork') as acme_net: + with mock.patch('certbot.main.client.acme_client.ClientNetwork') as acme_net: self._call_no_clientmock(args) os_ver = " ".join(le_util.get_os_info()) ua = acme_net.call_args[1]["user_agent"] @@ -171,7 +171,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if "linux" in plat.lower(): self.assertTrue(platform.linux_distribution()[0] in ua) - with mock.patch('letsencrypt.main.client.acme_client.ClientNetwork') as acme_net: + with mock.patch('certbot.main.client.acme_client.ClientNetwork') as acme_net: ua = "bandersnatch" args += ["--user-agent", ua] self._call_no_clientmock(args) @@ -183,7 +183,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods chain = 'chain' fullchain = 'fullchain' - with mock.patch('letsencrypt.main.install') as mock_install: + with mock.patch('certbot.main.install') as mock_install: self._call(['install', '--cert-path', cert, '--key-path', 'key', '--chain-path', 'chain', '--fullchain-path', 'fullchain']) @@ -194,14 +194,14 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(args.chain_path, os.path.abspath(chain)) self.assertEqual(args.fullchain_path, os.path.abspath(fullchain)) - @mock.patch('letsencrypt.main.plug_sel.record_chosen_plugins') - @mock.patch('letsencrypt.main.plug_sel.pick_installer') + @mock.patch('certbot.main.plug_sel.record_chosen_plugins') + @mock.patch('certbot.main.plug_sel.pick_installer') def test_installer_selection(self, mock_pick_installer, _rec): self._call(['install', '--domains', 'foo.bar', '--cert-path', 'cert', '--key-path', 'key', '--chain-path', 'chain']) self.assertEqual(mock_pick_installer.call_count, 1) - @mock.patch('letsencrypt.le_util.exe_exists') + @mock.patch('certbot.le_util.exe_exists') def test_configurator_selection(self, mock_exe_exists): mock_exe_exists.return_value = True real_plugins = disco.PluginsRegistry.find_all() @@ -210,7 +210,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods # This needed two calls to find_all(), which we're avoiding for now # because of possible side effects: # https://github.com/letsencrypt/letsencrypt/commit/51ed2b681f87b1eb29088dd48718a54f401e4855 - #with mock.patch('letsencrypt.cli.plugins_testable') as plugins: + #with mock.patch('certbot.cli.plugins_testable') as plugins: # plugins.return_value = {"apache": True, "nginx": True} # ret, _, _, _ = self._call(args) # self.assertTrue("Too many flags setting" in ret) @@ -220,21 +220,21 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods "example.com", "--debug"] if "nginx" in real_plugins: # Sending nginx a non-existent conf dir will simulate misconfiguration - # (we can only do that if letsencrypt-nginx is actually present) + # (we can only do that if certbot-nginx is actually present) ret, _, _, _ = self._call(args) self.assertTrue("The nginx plugin is not working" in ret) self.assertTrue("MisconfigurationError" in ret) self._cli_missing_flag(["--standalone"], "With the standalone plugin, you probably") - with mock.patch("letsencrypt.main._init_le_client") as mock_init: - with mock.patch("letsencrypt.main._auth_from_domains") as mock_afd: + with mock.patch("certbot.main._init_le_client") as mock_init: + with mock.patch("certbot.main._auth_from_domains") as mock_afd: mock_afd.return_value = (mock.MagicMock(), mock.MagicMock()) self._call(["certonly", "--manual", "-d", "foo.bar"]) unused_config, auth, unused_installer = mock_init.call_args[0] self.assertTrue(isinstance(auth, manual.Authenticator)) - with mock.patch('letsencrypt.main.obtain_cert') as mock_certonly: + with mock.patch('certbot.main.obtain_cert') as mock_certonly: self._call(["auth", "--standalone"]) self.assertEqual(1, mock_certonly.call_count) @@ -257,8 +257,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods for r in xrange(len(flags)))): self._call(['plugins'] + list(args)) - @mock.patch('letsencrypt.main.plugins_disco') - @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('certbot.main.plugins_disco') + @mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_no_args(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -269,8 +269,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods filtered = plugins.visible().ifaces() self.assertEqual(stdout.getvalue().strip(), str(filtered)) - @mock.patch('letsencrypt.main.plugins_disco') - @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('certbot.main.plugins_disco') + @mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_init(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -284,8 +284,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods verified = filtered.verify() self.assertEqual(stdout.getvalue().strip(), str(verified)) - @mock.patch('letsencrypt.main.plugins_disco') - @mock.patch('letsencrypt.main.cli.HelpfulArgumentParser.determine_help_topics') + @mock.patch('certbot.main.plugins_disco') + @mock.patch('certbot.main.cli.HelpfulArgumentParser.determine_help_topics') def test_plugins_prepare(self, _det, mock_disco): ifaces = [] plugins = mock_disco.PluginsRegistry.find_all() @@ -307,7 +307,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods chain = 'chain' fullchain = 'fullchain' - with mock.patch('letsencrypt.main.obtain_cert') as mock_obtaincert: + with mock.patch('certbot.main.obtain_cert') as mock_obtaincert: self._call(['certonly', '--cert-path', cert, '--key-path', 'key', '--chain-path', 'chain', '--fullchain-path', 'fullchain']) @@ -464,16 +464,16 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._check_server_conflict_message(short_args, conflicts) def _certonly_new_request_common(self, mock_client, args=None): - with mock.patch('letsencrypt.main._treat_as_renewal') as mock_renewal: + with mock.patch('certbot.main._treat_as_renewal') as mock_renewal: mock_renewal.return_value = ("newcert", None) - with mock.patch('letsencrypt.main._init_le_client') as mock_init: + with mock.patch('certbot.main._init_le_client') as mock_init: mock_init.return_value = mock_client if args is None: args = [] args += '-d foo.bar -a standalone certonly'.split() self._call(args) - @mock.patch('letsencrypt.main.zope.component.getUtility') + @mock.patch('certbot.main.zope.component.getUtility') def test_certonly_dry_run_new_request_success(self, mock_get_utility): mock_client = mock.MagicMock() mock_client.obtain_and_enroll_certificate.return_value = None @@ -485,8 +485,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods # Asserts we don't suggest donating after a successful dry run self.assertEqual(mock_get_utility().add_message.call_count, 1) - @mock.patch('letsencrypt.crypto_util.notAfter') - @mock.patch('letsencrypt.main.zope.component.getUtility') + @mock.patch('certbot.crypto_util.notAfter') + @mock.patch('certbot.main.zope.component.getUtility') def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter): cert_path = '/etc/letsencrypt/live/foo.bar' date = '1970-01-01' @@ -513,7 +513,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _test_renewal_common(self, due_for_renewal, extra_args, log_out=None, args=None, should_renew=True, error_expected=False): # pylint: disable=too-many-locals,too-many-arguments - cert_path = 'letsencrypt/tests/testdata/cert.pem' + cert_path = 'certbot/tests/testdata/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path) mock_lineage.should_autorenew.return_value = due_for_renewal @@ -524,17 +524,17 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_client.obtain_certificate.return_value = (mock_certr, 'chain', mock_key, 'csr') try: - with mock.patch('letsencrypt.main._find_duplicative_certs') as mock_fdc: + with mock.patch('certbot.main._find_duplicative_certs') as mock_fdc: mock_fdc.return_value = (mock_lineage, None) - with mock.patch('letsencrypt.main._init_le_client') as mock_init: + with mock.patch('certbot.main._init_le_client') as mock_init: mock_init.return_value = mock_client - get_utility_path = 'letsencrypt.main.zope.component.getUtility' + get_utility_path = 'certbot.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: - with mock.patch('letsencrypt.main.renewal.OpenSSL') as mock_ssl: + with mock.patch('certbot.main.renewal.OpenSSL') as mock_ssl: mock_latest = mock.MagicMock() mock_latest.get_issuer.return_value = "Fake fake" mock_ssl.crypto.load_certificate.return_value = mock_latest - with mock.patch('letsencrypt.main.renewal.crypto_util'): + with mock.patch('certbot.main.renewal.crypto_util'): if not args: args = ['-d', 'isnot.org', '-a', 'standalone', 'certonly'] if extra_args: @@ -625,7 +625,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual("", out) - @mock.patch("letsencrypt.cli.set_by_cli") + @mock.patch("certbot.cli.set_by_cli") def test_ancient_webroot_renewal_conf(self, mock_set_by_cli): mock_set_by_cli.return_value = False rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf') @@ -656,7 +656,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _test_renew_common(self, renewalparams=None, names=None, assert_oc_called=None, **kwargs): self._make_dummy_renewal_config() - with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: + with mock.patch('certbot.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() mock_lineage.fullchain = "somepath/fullchain.pem" if renewalparams is not None: @@ -664,7 +664,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods if names is not None: mock_lineage.names.return_value = names mock_rc.return_value = mock_lineage - with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: + with mock.patch('certbot.main.obtain_cert') as mock_obtain_cert: kwargs.setdefault('args', ['renew']) self._test_renewal_common(True, None, should_renew=False, **kwargs) @@ -714,19 +714,19 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_renew_reconstitute_error(self): # pylint: disable=protected-access - with mock.patch('letsencrypt.main.renewal._reconstitute') as mock_reconstitute: + with mock.patch('certbot.main.renewal._reconstitute') as mock_reconstitute: mock_reconstitute.side_effect = Exception self._test_renew_common(assert_oc_called=False, error_expected=True) def test_renew_obtain_cert_error(self): self._make_dummy_renewal_config() - with mock.patch('letsencrypt.storage.RenewableCert') as mock_rc: + with mock.patch('certbot.storage.RenewableCert') as mock_rc: mock_lineage = mock.MagicMock() mock_lineage.fullchain = "somewhere/fullchain.pem" mock_rc.return_value = mock_lineage mock_lineage.configuration = { 'renewalparams': {'authenticator': 'webroot'}} - with mock.patch('letsencrypt.main.obtain_cert') as mock_obtain_cert: + with mock.patch('certbot.main.obtain_cert') as mock_obtain_cert: mock_obtain_cert.side_effect = Exception self._test_renewal_common(True, None, error_expected=True, args=['renew'], should_renew=False) @@ -737,9 +737,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._test_renewal_common(True, None, args='renew --csr {0}'.format(CSR).split(), should_renew=False, error_expected=True) - @mock.patch('letsencrypt.main.zope.component.getUtility') - @mock.patch('letsencrypt.main._treat_as_renewal') - @mock.patch('letsencrypt.main._init_le_client') + @mock.patch('certbot.main.zope.component.getUtility') + @mock.patch('certbot.main._treat_as_renewal') + @mock.patch('certbot.main._init_le_client') def test_certonly_reinstall(self, mock_init, mock_renewal, mock_get_utility): mock_renewal.return_value = ('reinstall', mock.MagicMock()) mock_init.return_value = mock_client = mock.MagicMock() @@ -756,9 +756,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_client.obtain_certificate_from_csr.return_value = (certr, chain) cert_path = '/etc/letsencrypt/live/example.com/cert.pem' mock_client.save_certificate.return_value = cert_path, None, None - with mock.patch('letsencrypt.main._init_le_client') as mock_init: + with mock.patch('certbot.main._init_le_client') as mock_init: mock_init.return_value = mock_client - get_utility_path = 'letsencrypt.main.zope.component.getUtility' + get_utility_path = 'certbot.main.zope.component.getUtility' with mock.patch(get_utility_path) as mock_get_utility: chain_path = '/etc/letsencrypt/live/example.com/chain.pem' full_path = '/etc/letsencrypt/live/example.com/fullchain.pem' @@ -767,7 +767,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods CSR, cert_path, chain_path, full_path).split() if extra_args: args += extra_args - with mock.patch('letsencrypt.main.crypto_util'): + with mock.patch('certbot.main.crypto_util'): self._call(args) if '--dry-run' in args: @@ -791,7 +791,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue( 'dry run' in mock_get_utility().add_message.call_args[0][0]) - @mock.patch('letsencrypt.main.client.acme_client') + @mock.patch('certbot.main.client.acme_client') def test_revoke_with_key(self, mock_acme_client): server = 'foo.bar' self._call_no_clientmock(['--cert-path', CERT, '--key-path', KEY, @@ -804,7 +804,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_revoke = mock_acme_client.Client().revoke mock_revoke.assert_called_once_with(jose.ComparableX509(cert)) - @mock.patch('letsencrypt.main._determine_account') + @mock.patch('certbot.main._determine_account') def test_revoke_without_key(self, mock_determine_account): mock_determine_account.return_value = (mock.MagicMock(), None) _, _, _, client = self._call(['--cert-path', CERT, 'revoke']) @@ -813,7 +813,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_revoke = client.acme_from_config_key().revoke mock_revoke.assert_called_once_with(jose.ComparableX509(cert)) - @mock.patch('letsencrypt.main.sys') + @mock.patch('certbot.main.sys') def test_handle_exception(self, mock_sys): # pylint: disable=protected-access from acme import messages @@ -821,7 +821,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods config = mock.MagicMock() mock_open = mock.mock_open() - with mock.patch('letsencrypt.main.open', mock_open, create=True): + with mock.patch('certbot.main.open', mock_open, create=True): exception = Exception('detail') config.verbose_count = 1 main._handle_exception( @@ -831,7 +831,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods error_msg = mock_sys.exit.call_args_list[0][0][0] self.assertTrue('unexpected error' in error_msg) - with mock.patch('letsencrypt.main.open', mock_open, create=True): + with mock.patch('certbot.main.open', mock_open, create=True): mock_open.side_effect = [KeyboardInterrupt] error = errors.Error('detail') main._handle_exception( @@ -878,13 +878,13 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertEqual(contents, test_contents) def test_agree_dev_preview_config(self): - with mock.patch('letsencrypt.main.run') as mocked_run: + with mock.patch('certbot.main.run') as mocked_run: self._call(['-c', test_util.vector_path('cli.ini')]) self.assertTrue(mocked_run.called) class DetermineAccountTest(unittest.TestCase): - """Tests for letsencrypt.cli._determine_account.""" + """Tests for certbot.cli._determine_account.""" def setUp(self): self.args = mock.MagicMock(account=None, email=None, @@ -895,8 +895,8 @@ class DetermineAccountTest(unittest.TestCase): def _call(self): # pylint: disable=protected-access - from letsencrypt.main import _determine_account - with mock.patch('letsencrypt.main.account.AccountFileStorage') as mock_storage: + from certbot.main import _determine_account + with mock.patch('certbot.main.account.AccountFileStorage') as mock_storage: mock_storage.return_value = self.account_storage return _determine_account(self.config) @@ -913,7 +913,7 @@ class DetermineAccountTest(unittest.TestCase): self.assertEqual(self.accs[0].id, self.config.account) self.assertTrue(self.config.email is None) - @mock.patch('letsencrypt.client.display_ops.choose_account') + @mock.patch('certbot.client.display_ops.choose_account') def test_multiple_accounts(self, mock_choose_accounts): for acc in self.accs: self.account_storage.save(acc) @@ -924,11 +924,11 @@ class DetermineAccountTest(unittest.TestCase): self.assertEqual(self.accs[1].id, self.config.account) self.assertTrue(self.config.email is None) - @mock.patch('letsencrypt.client.display_ops.get_email') + @mock.patch('certbot.client.display_ops.get_email') def test_no_accounts_no_email(self, mock_get_email): mock_get_email.return_value = 'foo@bar.baz' - with mock.patch('letsencrypt.main.client') as client: + with mock.patch('certbot.main.client') as client: client.register.return_value = ( self.accs[0], mock.sentinel.acme) self.assertEqual((self.accs[0], mock.sentinel.acme), self._call()) @@ -940,7 +940,7 @@ class DetermineAccountTest(unittest.TestCase): def test_no_accounts_email(self): self.config.email = 'other email' - with mock.patch('letsencrypt.main.client') as client: + with mock.patch('certbot.main.client') as client: client.register.return_value = (self.accs[1], mock.sentinel.acme) self._call() self.assertEqual(self.accs[1].id, self.config.account) @@ -958,9 +958,9 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): def tearDown(self): shutil.rmtree(self.tempdir) - @mock.patch('letsencrypt.le_util.make_or_verify_dir') + @mock.patch('certbot.le_util.make_or_verify_dir') def test_find_duplicative_names(self, unused_makedir): - from letsencrypt.main import _find_duplicative_certs + from certbot.main import _find_duplicative_certs test_cert = test_util.load_vector('cert-san.pem') with open(self.test_rc.cert, 'w') as f: f.write(test_cert) @@ -989,7 +989,7 @@ class DuplicativeCertsTest(storage_test.BaseRenewableCertTest): class DefaultTest(unittest.TestCase): - """Tests for letsencrypt.cli._Default.""" + """Tests for certbot.cli._Default.""" def setUp(self): # pylint: disable=protected-access @@ -1008,7 +1008,7 @@ class DefaultTest(unittest.TestCase): class SetByCliTest(unittest.TestCase): - """Tests for letsencrypt.set_by_cli and related functions.""" + """Tests for certbot.set_by_cli and related functions.""" def setUp(self): reload_module(cli) @@ -1056,7 +1056,7 @@ class SetByCliTest(unittest.TestCase): def _call_set_by_cli(var, args, verb): - with mock.patch('letsencrypt.cli.helpful_parser') as mock_parser: + with mock.patch('certbot.cli.helpful_parser') as mock_parser: mock_parser.args = args mock_parser.verb = verb return cli.set_by_cli(var) diff --git a/letsencrypt/tests/client_test.py b/certbot/tests/client_test.py similarity index 88% rename from letsencrypt/tests/client_test.py rename to certbot/tests/client_test.py index cd6b11158..a41301148 100644 --- a/letsencrypt/tests/client_test.py +++ b/certbot/tests/client_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.client.""" +"""Tests for certbot.client.""" import os import shutil import tempfile @@ -9,11 +9,11 @@ import mock from acme import jose -from letsencrypt import account -from letsencrypt import errors -from letsencrypt import le_util +from certbot import account +from certbot import errors +from certbot import le_util -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = test_util.load_vector("rsa512_key.pem") @@ -30,7 +30,7 @@ class ConfigHelper(object): self.__dict__.update(kwds) class RegisterTest(unittest.TestCase): - """Tests for letsencrypt.client.register.""" + """Tests for certbot.client.register.""" def setUp(self): self.config = mock.MagicMock(rsa_key_size=1024, register_unsafely_without_email=False) @@ -38,13 +38,13 @@ class RegisterTest(unittest.TestCase): self.tos_cb = mock.MagicMock() def _call(self): - from letsencrypt.client import register + from certbot.client import register return register(self.config, self.account_storage, self.tos_cb) def test_no_tos(self): - with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: + with mock.patch("certbot.client.acme_client.Client") as mock_client: mock_client.register().terms_of_service = "http://tos" - with mock.patch("letsencrypt.account.report_new_account"): + with mock.patch("certbot.account.report_new_account"): self.tos_cb.return_value = False self.assertRaises(errors.Error, self._call) @@ -55,17 +55,17 @@ class RegisterTest(unittest.TestCase): self._call() def test_it(self): - with mock.patch("letsencrypt.client.acme_client.Client"): - with mock.patch("letsencrypt.account.report_new_account"): + with mock.patch("certbot.client.acme_client.Client"): + with mock.patch("certbot.account.report_new_account"): self._call() - @mock.patch("letsencrypt.account.report_new_account") - @mock.patch("letsencrypt.client.display_ops.get_email") + @mock.patch("certbot.account.report_new_account") + @mock.patch("certbot.client.display_ops.get_email") def test_email_retry(self, _rep, mock_get_email): from acme import messages msg = "DNS problem: NXDOMAIN looking up MX for example.com" mx_err = messages.Error(detail=msg, typ="urn:acme:error:invalidEmail") - with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: + with mock.patch("certbot.client.acme_client.Client") as mock_client: mock_client().register.side_effect = [mx_err, mock.MagicMock()] self._call() self.assertEqual(mock_get_email.call_count, 1) @@ -74,10 +74,10 @@ class RegisterTest(unittest.TestCase): self.config.email = None self.assertRaises(errors.Error, self._call) - @mock.patch("letsencrypt.client.logger") + @mock.patch("certbot.client.logger") def test_without_email(self, mock_logger): - with mock.patch("letsencrypt.client.acme_client.Client"): - with mock.patch("letsencrypt.account.report_new_account"): + with mock.patch("certbot.client.acme_client.Client"): + with mock.patch("certbot.account.report_new_account"): self.config.email = None self.config.register_unsafely_without_email = True self.config.dry_run = False @@ -88,12 +88,12 @@ class RegisterTest(unittest.TestCase): from acme import messages msg = "Test" mx_err = messages.Error(detail=msg, typ="malformed", title="title") - with mock.patch("letsencrypt.client.acme_client.Client") as mock_client: + with mock.patch("certbot.client.acme_client.Client") as mock_client: mock_client().register.side_effect = [mx_err, mock.MagicMock()] self.assertRaises(messages.Error, self._call) class ClientTest(unittest.TestCase): - """Tests for letsencrypt.client.Client.""" + """Tests for certbot.client.Client.""" def setUp(self): self.config = mock.MagicMock( @@ -102,8 +102,8 @@ class ClientTest(unittest.TestCase): self.account = mock.MagicMock(**{"key.pem": KEY}) self.eg_domains = ["example.com", "www.example.com"] - from letsencrypt.client import Client - with mock.patch("letsencrypt.client.acme_client.Client") as acme: + from certbot.client import Client + with mock.patch("certbot.client.acme_client.Client") as acme: self.acme_client = acme self.acme = acme.return_value = mock.MagicMock() self.client = Client( @@ -135,16 +135,16 @@ class ClientTest(unittest.TestCase): self.acme.fetch_chain.assert_called_once_with(mock.sentinel.certr) # FIXME move parts of this to test_cli.py... - @mock.patch("letsencrypt.client.logger") + @mock.patch("certbot.client.logger") def test_obtain_certificate_from_csr(self, mock_logger): self._mock_obtain_certificate() - from letsencrypt import cli + from certbot import cli test_csr = le_util.CSR(form="der", file=None, data=CSR_SAN) mock_parsed_args = mock.MagicMock() # The CLI should believe that this is a certonly request, because # a CSR would not be allowed with other kinds of requests! mock_parsed_args.verb = "certonly" - with mock.patch("letsencrypt.client.le_util.CSR") as mock_CSR: + with mock.patch("certbot.client.le_util.CSR") as mock_CSR: mock_CSR.return_value = test_csr mock_parsed_args.domains = self.eg_domains[:] mock_parser = mock.MagicMock(cli.HelpfulArgumentParser) @@ -186,7 +186,7 @@ class ClientTest(unittest.TestCase): test_csr) mock_logger.warning.assert_called_once_with(mock.ANY) - @mock.patch("letsencrypt.client.crypto_util") + @mock.patch("certbot.client.crypto_util") def test_obtain_certificate(self, mock_crypto_util): self._mock_obtain_certificate() @@ -290,7 +290,7 @@ class ClientTest(unittest.TestCase): ["foo.bar"], "key", "cert", "chain", "fullchain") installer.recovery_routine.assert_called_once_with() - @mock.patch("letsencrypt.client.zope.component.getUtility") + @mock.patch("certbot.client.zope.component.getUtility") def test_deploy_certificate_restart_failure(self, mock_get_utility): installer = mock.MagicMock() installer.restart.side_effect = [errors.PluginError, None] @@ -302,7 +302,7 @@ class ClientTest(unittest.TestCase): installer.rollback_checkpoints.assert_called_once_with() self.assertEqual(installer.restart.call_count, 2) - @mock.patch("letsencrypt.client.zope.component.getUtility") + @mock.patch("certbot.client.zope.component.getUtility") def test_deploy_certificate_restart_failure2(self, mock_get_utility): installer = mock.MagicMock() installer.restart.side_effect = errors.PluginError @@ -315,7 +315,7 @@ class ClientTest(unittest.TestCase): installer.rollback_checkpoints.assert_called_once_with() self.assertEqual(installer.restart.call_count, 1) - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.enhancements") def test_enhance_config(self, mock_enhancements): config = ConfigHelper(redirect=True, hsts=False, uir=False) self.assertRaises(errors.Error, @@ -331,7 +331,7 @@ class ClientTest(unittest.TestCase): self.assertEqual(installer.save.call_count, 1) installer.restart.assert_called_once_with() - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.enhancements") def test_enhance_config_no_ask(self, mock_enhancements): config = ConfigHelper(redirect=True, hsts=False, uir=False) self.assertRaises(errors.Error, @@ -359,7 +359,7 @@ class ClientTest(unittest.TestCase): self.assertEqual(installer.save.call_count, 3) self.assertEqual(installer.restart.call_count, 3) - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.enhancements") def test_enhance_config_unsupported(self, mock_enhancements): installer = mock.MagicMock() self.client.installer = installer @@ -375,8 +375,8 @@ class ClientTest(unittest.TestCase): self.assertRaises(errors.Error, self.client.enhance_config, ["foo.bar"], config) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_enhance_failure(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -392,8 +392,8 @@ class ClientTest(unittest.TestCase): installer.recovery_routine.assert_called_once_with() self.assertEqual(mock_get_utility().add_message.call_count, 1) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_save_failure(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -409,8 +409,8 @@ class ClientTest(unittest.TestCase): installer.recovery_routine.assert_called_once_with() self.assertEqual(mock_get_utility().add_message.call_count, 1) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_restart_failure(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -428,8 +428,8 @@ class ClientTest(unittest.TestCase): installer.rollback_checkpoints.assert_called_once_with() self.assertEqual(installer.restart.call_count, 2) - @mock.patch("letsencrypt.client.zope.component.getUtility") - @mock.patch("letsencrypt.client.enhancements") + @mock.patch("certbot.client.zope.component.getUtility") + @mock.patch("certbot.client.enhancements") def test_enhance_config_restart_failure2(self, mock_enhancements, mock_get_utility): mock_enhancements.ask.return_value = True @@ -449,15 +449,15 @@ class ClientTest(unittest.TestCase): class RollbackTest(unittest.TestCase): - """Tests for letsencrypt.client.rollback.""" + """Tests for certbot.client.rollback.""" def setUp(self): self.m_install = mock.MagicMock() @classmethod def _call(cls, checkpoints, side_effect): - from letsencrypt.client import rollback - with mock.patch("letsencrypt.client.plugin_selection.pick_installer") as mpi: + from certbot.client import rollback + with mock.patch("certbot.client.plugin_selection.pick_installer") as mpi: mpi.side_effect = side_effect rollback(None, checkpoints, {}, mock.MagicMock()) diff --git a/letsencrypt/tests/colored_logging_test.py b/certbot/tests/colored_logging_test.py similarity index 85% rename from letsencrypt/tests/colored_logging_test.py rename to certbot/tests/colored_logging_test.py index 4080157fc..91c6b8c08 100644 --- a/letsencrypt/tests/colored_logging_test.py +++ b/certbot/tests/colored_logging_test.py @@ -1,17 +1,17 @@ -"""Tests for letsencrypt.colored_logging.""" +"""Tests for certbot.colored_logging.""" import logging import unittest import six -from letsencrypt import le_util +from certbot import le_util class StreamHandlerTest(unittest.TestCase): - """Tests for letsencrypt.colored_logging.""" + """Tests for certbot.colored_logging.""" def setUp(self): - from letsencrypt import colored_logging + from certbot import colored_logging self.stream = six.StringIO() self.stream.isatty = lambda: True diff --git a/letsencrypt/tests/configuration_test.py b/certbot/tests/configuration_test.py similarity index 87% rename from letsencrypt/tests/configuration_test.py rename to certbot/tests/configuration_test.py index a4f881d34..13d85bd9f 100644 --- a/letsencrypt/tests/configuration_test.py +++ b/certbot/tests/configuration_test.py @@ -1,26 +1,26 @@ -"""Tests for letsencrypt.configuration.""" +"""Tests for certbot.configuration.""" import os import unittest import mock -from letsencrypt import errors +from certbot import errors class NamespaceConfigTest(unittest.TestCase): - """Tests for letsencrypt.configuration.NamespaceConfig.""" + """Tests for certbot.configuration.NamespaceConfig.""" def setUp(self): self.namespace = mock.MagicMock( config_dir='/tmp/config', work_dir='/tmp/foo', foo='bar', server='https://acme-server.org:443/new', tls_sni_01_port=1234, http01_port=4321) - from letsencrypt.configuration import NamespaceConfig + from certbot.configuration import NamespaceConfig self.config = NamespaceConfig(self.namespace) def test_init_same_ports(self): self.namespace.tls_sni_01_port = 4321 - from letsencrypt.configuration import NamespaceConfig + from certbot.configuration import NamespaceConfig self.assertRaises(errors.Error, NamespaceConfig, self.namespace) def test_proxy_getattr(self): @@ -36,7 +36,7 @@ class NamespaceConfigTest(unittest.TestCase): self.assertEqual(['user:pass@acme.server:443', 'p', 'a', 't', 'h'], self.config.server_path.split(os.path.sep)) - @mock.patch('letsencrypt.configuration.constants') + @mock.patch('certbot.configuration.constants') def test_dynamic_dirs(self, constants): constants.ACCOUNTS_DIR = 'acc' constants.BACKUP_DIR = 'backups' @@ -55,7 +55,7 @@ class NamespaceConfigTest(unittest.TestCase): self.assertEqual(self.config.temp_checkpoint_dir, '/tmp/foo/t') def test_absolute_paths(self): - from letsencrypt.configuration import NamespaceConfig + from certbot.configuration import NamespaceConfig config_base = "foo" work_base = "bar" @@ -88,14 +88,14 @@ class NamespaceConfigTest(unittest.TestCase): class RenewerConfigurationTest(unittest.TestCase): - """Test for letsencrypt.configuration.RenewerConfiguration.""" + """Test for certbot.configuration.RenewerConfiguration.""" def setUp(self): self.namespace = mock.MagicMock(config_dir='/tmp/config') - from letsencrypt.configuration import RenewerConfiguration + from certbot.configuration import RenewerConfiguration self.config = RenewerConfiguration(self.namespace) - @mock.patch('letsencrypt.configuration.constants') + @mock.patch('certbot.configuration.constants') def test_dynamic_dirs(self, constants): constants.ARCHIVE_DIR = 'a' constants.LIVE_DIR = 'l' @@ -109,8 +109,8 @@ class RenewerConfigurationTest(unittest.TestCase): self.assertEqual(self.config.renewer_config_file, '/tmp/config/r.conf') def test_absolute_paths(self): - from letsencrypt.configuration import NamespaceConfig - from letsencrypt.configuration import RenewerConfiguration + from certbot.configuration import NamespaceConfig + from certbot.configuration import RenewerConfiguration config_base = "foo" work_base = "bar" diff --git a/letsencrypt/tests/crypto_util_test.py b/certbot/tests/crypto_util_test.py similarity index 74% rename from letsencrypt/tests/crypto_util_test.py rename to certbot/tests/crypto_util_test.py index 1a9f39572..52e595577 100644 --- a/letsencrypt/tests/crypto_util_test.py +++ b/certbot/tests/crypto_util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.crypto_util.""" +"""Tests for certbot.crypto_util.""" import logging import shutil import tempfile @@ -8,9 +8,9 @@ import OpenSSL import mock import zope.component -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt.tests import test_util +from certbot import errors +from certbot import interfaces +from certbot.tests import test_util RSA256_KEY = test_util.load_vector('rsa256_key.pem') @@ -21,7 +21,7 @@ SAN_CERT = test_util.load_vector('cert-san.pem') class InitSaveKeyTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.init_save_key.""" + """Tests for certbot.crypto_util.init_save_key.""" def setUp(self): logging.disable(logging.CRITICAL) zope.component.provideUtility( @@ -34,24 +34,24 @@ class InitSaveKeyTest(unittest.TestCase): @classmethod def _call(cls, key_size, key_dir): - from letsencrypt.crypto_util import init_save_key - return init_save_key(key_size, key_dir, 'key-letsencrypt.pem') + from certbot.crypto_util import init_save_key + return init_save_key(key_size, key_dir, 'key-certbot.pem') - @mock.patch('letsencrypt.crypto_util.make_key') + @mock.patch('certbot.crypto_util.make_key') def test_success(self, mock_make): mock_make.return_value = 'key_pem' key = self._call(1024, self.key_dir) self.assertEqual(key.pem, 'key_pem') - self.assertTrue('key-letsencrypt.pem' in key.file) + self.assertTrue('key-certbot.pem' in key.file) - @mock.patch('letsencrypt.crypto_util.make_key') + @mock.patch('certbot.crypto_util.make_key') def test_key_failure(self, mock_make): mock_make.side_effect = ValueError self.assertRaises(ValueError, self._call, 431, self.key_dir) class InitSaveCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.init_save_csr.""" + """Tests for certbot.crypto_util.init_save_csr.""" def setUp(self): zope.component.provideUtility( @@ -61,31 +61,31 @@ class InitSaveCSRTest(unittest.TestCase): def tearDown(self): shutil.rmtree(self.csr_dir) - @mock.patch('letsencrypt.crypto_util.make_csr') - @mock.patch('letsencrypt.crypto_util.le_util.make_or_verify_dir') + @mock.patch('certbot.crypto_util.make_csr') + @mock.patch('certbot.crypto_util.le_util.make_or_verify_dir') def test_it(self, unused_mock_verify, mock_csr): - from letsencrypt.crypto_util import init_save_csr + from certbot.crypto_util import init_save_csr mock_csr.return_value = ('csr_pem', 'csr_der') csr = init_save_csr( mock.Mock(pem='dummy_key'), 'example.com', self.csr_dir, - 'csr-letsencrypt.pem') + 'csr-certbot.pem') self.assertEqual(csr.data, 'csr_der') - self.assertTrue('csr-letsencrypt.pem' in csr.file) + self.assertTrue('csr-certbot.pem' in csr.file) class MakeCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.make_csr.""" + """Tests for certbot.crypto_util.make_csr.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import make_csr + from certbot.crypto_util import make_csr return make_csr(*args, **kwargs) def test_san(self): - from letsencrypt.crypto_util import get_sans_from_csr + from certbot.crypto_util import get_sans_from_csr # TODO: Fails for RSA256_KEY csr_pem, csr_der = self._call( RSA512_KEY, ['example.com', 'www.example.com']) @@ -97,11 +97,11 @@ class MakeCSRTest(unittest.TestCase): class ValidCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.valid_csr.""" + """Tests for certbot.crypto_util.valid_csr.""" @classmethod def _call(cls, csr): - from letsencrypt.crypto_util import valid_csr + from certbot.crypto_util import valid_csr return valid_csr(csr) def test_valid_pem_true(self): @@ -124,11 +124,11 @@ class ValidCSRTest(unittest.TestCase): class CSRMatchesPubkeyTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.csr_matches_pubkey.""" + """Tests for certbot.crypto_util.csr_matches_pubkey.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import csr_matches_pubkey + from certbot.crypto_util import csr_matches_pubkey return csr_matches_pubkey(*args, **kwargs) def test_valid_true(self): @@ -141,21 +141,21 @@ class CSRMatchesPubkeyTest(unittest.TestCase): class MakeKeyTest(unittest.TestCase): # pylint: disable=too-few-public-methods - """Tests for letsencrypt.crypto_util.make_key.""" + """Tests for certbot.crypto_util.make_key.""" def test_it(self): # pylint: disable=no-self-use - from letsencrypt.crypto_util import make_key + from certbot.crypto_util import make_key # Do not test larger keys as it takes too long. OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, make_key(1024)) class ValidPrivkeyTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.valid_privkey.""" + """Tests for certbot.crypto_util.valid_privkey.""" @classmethod def _call(cls, privkey): - from letsencrypt.crypto_util import valid_privkey + from certbot.crypto_util import valid_privkey return valid_privkey(privkey) def test_valid_true(self): @@ -169,11 +169,11 @@ class ValidPrivkeyTest(unittest.TestCase): class GetSANsFromCertTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.get_sans_from_cert.""" + """Tests for certbot.crypto_util.get_sans_from_cert.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import get_sans_from_cert + from certbot.crypto_util import get_sans_from_cert return get_sans_from_cert(*args, **kwargs) def test_single(self): @@ -186,11 +186,11 @@ class GetSANsFromCertTest(unittest.TestCase): class GetSANsFromCSRTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.get_sans_from_csr.""" + """Tests for certbot.crypto_util.get_sans_from_csr.""" @classmethod def _call(cls, *args, **kwargs): - from letsencrypt.crypto_util import get_sans_from_csr + from certbot.crypto_util import get_sans_from_csr return get_sans_from_csr(*args, **kwargs) def test_extract_one_san(self): @@ -216,36 +216,36 @@ class GetSANsFromCSRTest(unittest.TestCase): class CertLoaderTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.pyopenssl_load_certificate""" + """Tests for certbot.crypto_util.pyopenssl_load_certificate""" def test_load_valid_cert(self): - from letsencrypt.crypto_util import pyopenssl_load_certificate + from certbot.crypto_util import pyopenssl_load_certificate cert, file_type = pyopenssl_load_certificate(CERT) self.assertEqual(cert.digest('sha1'), OpenSSL.crypto.load_certificate(file_type, CERT).digest('sha1')) def test_load_invalid_cert(self): - from letsencrypt.crypto_util import pyopenssl_load_certificate + from certbot.crypto_util import pyopenssl_load_certificate bad_cert_data = CERT.replace("BEGIN CERTIFICATE", "ASDFASDFASDF!!!") self.assertRaises( errors.Error, pyopenssl_load_certificate, bad_cert_data) class NotBeforeTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.notBefore""" + """Tests for certbot.crypto_util.notBefore""" def test_notBefore(self): - from letsencrypt.crypto_util import notBefore + from certbot.crypto_util import notBefore self.assertEqual(notBefore(CERT_PATH).isoformat(), '2014-12-11T22:34:45+00:00') class NotAfterTest(unittest.TestCase): - """Tests for letsencrypt.crypto_util.notAfter""" + """Tests for certbot.crypto_util.notAfter""" def test_notAfter(self): - from letsencrypt.crypto_util import notAfter + from certbot.crypto_util import notAfter self.assertEqual(notAfter(CERT_PATH).isoformat(), '2014-12-18T22:34:45+00:00') diff --git a/letsencrypt/tests/display/__init__.py b/certbot/tests/display/__init__.py similarity index 100% rename from letsencrypt/tests/display/__init__.py rename to certbot/tests/display/__init__.py diff --git a/letsencrypt/tests/display/completer_test.py b/certbot/tests/display/completer_test.py similarity index 88% rename from letsencrypt/tests/display/completer_test.py rename to certbot/tests/display/completer_test.py index 3c181c925..16805314c 100644 --- a/letsencrypt/tests/display/completer_test.py +++ b/certbot/tests/display/completer_test.py @@ -1,4 +1,4 @@ -"""Test letsencrypt.display.completer.""" +"""Test certbot.display.completer.""" import os import readline import shutil @@ -12,7 +12,7 @@ from six.moves import reload_module # pylint: disable=import-error class CompleterTest(unittest.TestCase): - """Test letsencrypt.display.completer.Completer.""" + """Test certbot.display.completer.Completer.""" def setUp(self): self.temp_dir = tempfile.mkdtemp() @@ -37,7 +37,7 @@ class CompleterTest(unittest.TestCase): shutil.rmtree(self.temp_dir) def test_complete(self): - from letsencrypt.display import completer + from certbot.display import completer my_completer = completer.Completer() num_paths = len(self.paths) @@ -59,7 +59,7 @@ class CompleterTest(unittest.TestCase): sys.modules['readline'] = original_readline def test_context_manager_with_unmocked_readline(self): - from letsencrypt.display import completer + from certbot.display import completer reload_module(completer) original_completer = readline.get_completer() @@ -71,18 +71,18 @@ class CompleterTest(unittest.TestCase): self.assertEqual(readline.get_completer(), original_completer) self.assertEqual(readline.get_completer_delims(), original_delims) - @mock.patch('letsencrypt.display.completer.readline', autospec=True) + @mock.patch('certbot.display.completer.readline', autospec=True) def test_context_manager_libedit(self, mock_readline): mock_readline.__doc__ = 'libedit' self._test_context_manager_with_mock_readline(mock_readline) - @mock.patch('letsencrypt.display.completer.readline', autospec=True) + @mock.patch('certbot.display.completer.readline', autospec=True) def test_context_manager_readline(self, mock_readline): mock_readline.__doc__ = 'GNU readline' self._test_context_manager_with_mock_readline(mock_readline) def _test_context_manager_with_mock_readline(self, mock_readline): - from letsencrypt.display import completer + from certbot.display import completer mock_readline.parse_and_bind.side_effect = enable_tab_completion diff --git a/letsencrypt/tests/display/enhancements_test.py b/certbot/tests/display/enhancements_test.py similarity index 74% rename from letsencrypt/tests/display/enhancements_test.py rename to certbot/tests/display/enhancements_test.py index 6375316bf..b8321d940 100644 --- a/letsencrypt/tests/display/enhancements_test.py +++ b/certbot/tests/display/enhancements_test.py @@ -4,8 +4,8 @@ import unittest import mock -from letsencrypt import errors -from letsencrypt.display import util as display_util +from certbot import errors +from certbot.display import util as display_util class AskTest(unittest.TestCase): @@ -18,10 +18,10 @@ class AskTest(unittest.TestCase): @classmethod def _call(cls, enhancement): - from letsencrypt.display.enhancements import ask + from certbot.display.enhancements import ask return ask(enhancement) - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_redirect(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertTrue(self._call("redirect")) @@ -34,20 +34,20 @@ class RedirectTest(unittest.TestCase): """Test the redirect_by_default method.""" @classmethod def _call(cls): - from letsencrypt.display.enhancements import redirect_by_default + from certbot.display.enhancements import redirect_by_default return redirect_by_default() - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_secure(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertTrue(self._call()) - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_cancel(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 1) self.assertFalse(self._call()) - @mock.patch("letsencrypt.display.enhancements.util") + @mock.patch("certbot.display.enhancements.util") def test_easy(self, mock_util): mock_util().menu.return_value = (display_util.OK, 0) self.assertFalse(self._call()) diff --git a/letsencrypt/tests/display/ops_test.py b/certbot/tests/display/ops_test.py similarity index 85% rename from letsencrypt/tests/display/ops_test.py rename to certbot/tests/display/ops_test.py index 0dacdfea8..05cb6b12d 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/certbot/tests/display/ops_test.py @@ -1,5 +1,5 @@ # coding=utf-8 -"""Test letsencrypt.display.ops.""" +"""Test certbot.display.ops.""" import os import sys import tempfile @@ -11,19 +11,19 @@ import zope.component from acme import jose from acme import messages -from letsencrypt import account -from letsencrypt import interfaces +from certbot import account +from certbot import interfaces -from letsencrypt.display import util as display_util +from certbot.display import util as display_util -from letsencrypt.tests import test_util +from certbot.tests import test_util KEY = jose.JWKRSA.load(test_util.load_vector("rsa512_key.pem")) class GetEmailTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.get_email.""" + """Tests for certbot.display.ops.get_email.""" def setUp(self): mock_display = mock.MagicMock() @@ -32,7 +32,7 @@ class GetEmailTest(unittest.TestCase): @classmethod def _call(cls, **kwargs): - from letsencrypt.display.ops import get_email + from certbot.display.ops import get_email return get_email(**kwargs) def test_cancel_none(self): @@ -41,13 +41,13 @@ class GetEmailTest(unittest.TestCase): def test_ok_safe(self): self.input.return_value = (display_util.OK, "foo@bar.baz") - with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email: + with mock.patch("certbot.display.ops.le_util.safe_email") as mock_safe_email: mock_safe_email.return_value = True self.assertTrue(self._call() is "foo@bar.baz") def test_ok_not_safe(self): self.input.return_value = (display_util.OK, "foo@bar.baz") - with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email: + with mock.patch("certbot.display.ops.le_util.safe_email") as mock_safe_email: mock_safe_email.side_effect = [False, True] self.assertTrue(self._call() is "foo@bar.baz") @@ -56,7 +56,7 @@ class GetEmailTest(unittest.TestCase): invalid_txt = "There seem to be problems" base_txt = "Enter email" self.input.return_value = (display_util.OK, "foo@bar.baz") - with mock.patch("letsencrypt.display.ops.le_util.safe_email") as mock_safe_email: + with mock.patch("certbot.display.ops.le_util.safe_email") as mock_safe_email: mock_safe_email.return_value = True self._call() msg = self.input.call_args[0][0] @@ -75,7 +75,7 @@ class GetEmailTest(unittest.TestCase): class ChooseAccountTest(unittest.TestCase): - """Tests for letsencrypt.display.ops.choose_account.""" + """Tests for certbot.display.ops.choose_account.""" def setUp(self): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -86,7 +86,7 @@ class ChooseAccountTest(unittest.TestCase): self.config = mock.MagicMock( accounts_dir=self.accounts_dir, account_keys_dir=self.account_keys_dir, - server="letsencrypt-demo.org") + server="certbot-demo.org") self.key = KEY self.acc1 = account.Account(messages.RegistrationResource( @@ -98,20 +98,20 @@ class ChooseAccountTest(unittest.TestCase): @classmethod def _call(cls, accounts): - from letsencrypt.display import ops + from certbot.display import ops return ops.choose_account(accounts) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_one(self, mock_util): mock_util().menu.return_value = (display_util.OK, 0) self.assertEqual(self._call([self.acc1]), self.acc1) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_two(self, mock_util): mock_util().menu.return_value = (display_util.OK, 1) self.assertEqual(self._call([self.acc1, self.acc2]), self.acc2) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_cancel(self, mock_util): mock_util().menu.return_value = (display_util.CANCEL, 1) self.assertTrue(self._call([self.acc1, self.acc2]) is None) @@ -124,7 +124,7 @@ class GenSSLLabURLs(unittest.TestCase): @classmethod def _call(cls, domains): - from letsencrypt.display.ops import _gen_ssl_lab_urls + from certbot.display.ops import _gen_ssl_lab_urls return _gen_ssl_lab_urls(domains) def test_zero(self): @@ -143,7 +143,7 @@ class GenHttpsNamesTest(unittest.TestCase): @classmethod def _call(cls, domains): - from letsencrypt.display.ops import _gen_https_names + from certbot.display.ops import _gen_https_names return _gen_https_names(domains) def test_zero(self): @@ -191,20 +191,20 @@ class ChooseNamesTest(unittest.TestCase): @classmethod def _call(cls, installer): - from letsencrypt.display.ops import choose_names + from certbot.display.ops import choose_names return choose_names(installer) - @mock.patch("letsencrypt.display.ops._choose_names_manually") + @mock.patch("certbot.display.ops._choose_names_manually") def test_no_installer(self, mock_manual): self._call(None) self.assertEqual(mock_manual.call_count, 1) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_no_installer_cancel(self, mock_util): mock_util().input.return_value = (display_util.CANCEL, []) self.assertEqual(self._call(None), []) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_no_names_choose(self, mock_util): self.mock_install().get_all_names.return_value = set() mock_util().yesno.return_value = True @@ -215,14 +215,14 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(mock_util().input.call_count, 1) self.assertEqual(actual_doms, [domain]) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_no_names_quit(self, mock_util): self.mock_install().get_all_names.return_value = set() mock_util().yesno.return_value = False self.assertEqual(self._call(self.mock_install), []) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_filter_names_valid_return(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = (display_util.OK, ["example.com"]) @@ -231,14 +231,14 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(names, ["example.com"]) self.assertEqual(mock_util().checklist.call_count, 1) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_filter_names_nothing_selected(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = (display_util.OK, []) self.assertEqual(self._call(self.mock_install), []) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_filter_names_cancel(self, mock_util): self.mock_install.get_all_names.return_value = set(["example.com"]) mock_util().checklist.return_value = ( @@ -247,7 +247,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(self._call(self.mock_install), []) def test_get_valid_domains(self): - from letsencrypt.display.ops import get_valid_domains + from certbot.display.ops import get_valid_domains all_valid = ["example.com", "second.example.com", "also.example.com"] all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN", @@ -257,9 +257,9 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(get_valid_domains(all_invalid), []) self.assertEqual(len(get_valid_domains(two_valid)), 2) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_choose_manually(self, mock_util): - from letsencrypt.display.ops import _choose_names_manually + from certbot.display.ops import _choose_names_manually # No retry mock_util().yesno.return_value = False # IDN and no retry @@ -268,7 +268,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(_choose_names_manually(), []) # IDN exception with previous mocks with mock.patch( - "letsencrypt.display.ops.display_util.separate_list_input" + "certbot.display.ops.display_util.separate_list_input" ) as mock_sli: unicode_error = UnicodeEncodeError('mock', u'', 0, 1, 'mock') mock_sli.side_effect = unicode_error @@ -302,10 +302,10 @@ class SuccessInstallationTest(unittest.TestCase): """Test the success installation message.""" @classmethod def _call(cls, names): - from letsencrypt.display.ops import success_installation + from certbot.display.ops import success_installation success_installation(names) - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_success_installation(self, mock_util): mock_util().notification.return_value = None names = ["example.com", "abc.com"] @@ -324,10 +324,10 @@ class SuccessRenewalTest(unittest.TestCase): """Test the success renewal message.""" @classmethod def _call(cls, names): - from letsencrypt.display.ops import success_renewal + from certbot.display.ops import success_renewal success_renewal(names, "renew") - @mock.patch("letsencrypt.display.ops.z_util") + @mock.patch("certbot.display.ops.z_util") def test_success_renewal(self, mock_util): mock_util().notification.return_value = None names = ["example.com", "abc.com"] diff --git a/letsencrypt/tests/display/util_test.py b/certbot/tests/display/util_test.py similarity index 91% rename from letsencrypt/tests/display/util_test.py rename to certbot/tests/display/util_test.py index bae0d582a..4a38803d1 100644 --- a/letsencrypt/tests/display/util_test.py +++ b/certbot/tests/display/util_test.py @@ -1,12 +1,12 @@ -"""Test :mod:`letsencrypt.display.util`.""" +"""Test :mod:`certbot.display.util`.""" import os import unittest import mock -import letsencrypt.errors as errors +import certbot.errors as errors -from letsencrypt.display import util as display_util +from certbot.display import util as display_util CHOICES = [("First", "Description1"), ("Second", "Description2")] @@ -40,13 +40,13 @@ class NcursesDisplayTest(unittest.TestCase): "menu_height": display_util.HEIGHT - 6, } - @mock.patch("letsencrypt.display.util.dialog.Dialog.msgbox") + @mock.patch("certbot.display.util.dialog.Dialog.msgbox") def test_notification(self, mock_msgbox): """Kind of worthless... one liner.""" self.displayer.notification("message") self.assertEqual(mock_msgbox.call_count, 1) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_tag_and_desc(self, mock_menu): mock_menu.return_value = (display_util.OK, "First") @@ -55,7 +55,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.OK, 0)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_tag_and_desc_cancel(self, mock_menu): mock_menu.return_value = (display_util.CANCEL, "") @@ -65,7 +65,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.CANCEL, -1)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_desc_only(self, mock_menu): mock_menu.return_value = (display_util.OK, "1") @@ -77,7 +77,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.OK, 0)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_desc_only_help(self, mock_menu): mock_menu.return_value = (display_util.HELP, "2") @@ -85,7 +85,7 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.HELP, 1)) - @mock.patch("letsencrypt.display.util.dialog.Dialog.menu") + @mock.patch("certbot.display.util.dialog.Dialog.menu") def test_menu_desc_only_cancel(self, mock_menu): mock_menu.return_value = (display_util.CANCEL, "") @@ -93,13 +93,13 @@ class NcursesDisplayTest(unittest.TestCase): self.assertEqual(ret, (display_util.CANCEL, -1)) - @mock.patch("letsencrypt.display.util." + @mock.patch("certbot.display.util." "dialog.Dialog.inputbox") def test_input(self, mock_input): self.displayer.input("message") self.assertEqual(mock_input.call_count, 1) - @mock.patch("letsencrypt.display.util.dialog.Dialog.yesno") + @mock.patch("certbot.display.util.dialog.Dialog.yesno") def test_yesno(self, mock_yesno): mock_yesno.return_value = display_util.OK @@ -109,7 +109,7 @@ class NcursesDisplayTest(unittest.TestCase): "message", display_util.HEIGHT, display_util.WIDTH, yes_label="Yes", no_label="No") - @mock.patch("letsencrypt.display.util." + @mock.patch("certbot.display.util." "dialog.Dialog.checklist") def test_checklist(self, mock_checklist): self.displayer.checklist("message", TAGS) @@ -123,7 +123,7 @@ class NcursesDisplayTest(unittest.TestCase): "message", width=display_util.WIDTH, height=display_util.HEIGHT, choices=choices) - @mock.patch("letsencrypt.display.util.dialog.Dialog.dselect") + @mock.patch("certbot.display.util.dialog.Dialog.dselect") def test_directory_select(self, mock_dselect): self.displayer.directory_select("message") self.assertEqual(mock_dselect.call_count, 1) @@ -153,7 +153,7 @@ class FileOutputDisplayTest(unittest.TestCase): self.assertTrue("message" in self.mock_stdout.write.call_args[0][0]) - @mock.patch("letsencrypt.display.util." + @mock.patch("certbot.display.util." "FileDisplay._get_valid_int_ans") def test_menu(self, mock_ans): mock_ans.return_value = (display_util.OK, 1) @@ -188,14 +188,14 @@ class FileOutputDisplayTest(unittest.TestCase): with mock.patch("__builtin__.raw_input", return_value="a"): self.assertTrue(self.displayer.yesno("msg", yes_label="Agree")) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_checklist_valid(self, mock_input): mock_input.return_value = (display_util.OK, "2 1") code, tag_list = self.displayer.checklist("msg", TAGS) self.assertEqual( (code, set(tag_list)), (display_util.OK, set(["tag1", "tag2"]))) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_checklist_miss_valid(self, mock_input): mock_input.side_effect = [ (display_util.OK, "10"), @@ -206,7 +206,7 @@ class FileOutputDisplayTest(unittest.TestCase): ret = self.displayer.checklist("msg", TAGS) self.assertEqual(ret, (display_util.OK, ["tag1"])) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_checklist_miss_quit(self, mock_input): mock_input.side_effect = [ (display_util.OK, "10"), @@ -232,7 +232,7 @@ class FileOutputDisplayTest(unittest.TestCase): self.displayer._scrub_checklist_input(list_, TAGS)) self.assertEqual(set_tags, exp[i]) - @mock.patch("letsencrypt.display.util.FileDisplay.input") + @mock.patch("certbot.display.util.FileDisplay.input") def test_directory_select(self, mock_input): message = "msg" result = (display_util.OK, "/var/www/html",) @@ -352,7 +352,7 @@ class SeparateListInputTest(unittest.TestCase): @classmethod def _call(cls, input_): - from letsencrypt.display.util import separate_list_input + from certbot.display.util import separate_list_input return separate_list_input(input_) def test_commas(self): @@ -378,7 +378,7 @@ class SeparateListInputTest(unittest.TestCase): class PlaceParensTest(unittest.TestCase): @classmethod def _call(cls, label): # pylint: disable=protected-access - from letsencrypt.display.util import _parens_around_char + from certbot.display.util import _parens_around_char return _parens_around_char(label) def test_single_letter(self): diff --git a/letsencrypt/tests/error_handler_test.py b/certbot/tests/error_handler_test.py similarity index 90% rename from letsencrypt/tests/error_handler_test.py rename to certbot/tests/error_handler_test.py index 7fbdcffd8..5434b36be 100644 --- a/letsencrypt/tests/error_handler_test.py +++ b/certbot/tests/error_handler_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.error_handler.""" +"""Tests for certbot.error_handler.""" import signal import sys import unittest @@ -7,10 +7,10 @@ import mock class ErrorHandlerTest(unittest.TestCase): - """Tests for letsencrypt.error_handler.""" + """Tests for certbot.error_handler.""" def setUp(self): - from letsencrypt import error_handler + from certbot import error_handler self.init_func = mock.MagicMock() self.init_args = set((42,)) @@ -30,8 +30,8 @@ class ErrorHandlerTest(unittest.TestCase): self.init_func.assert_called_once_with(*self.init_args, **self.init_kwargs) - @mock.patch('letsencrypt.error_handler.os') - @mock.patch('letsencrypt.error_handler.signal') + @mock.patch('certbot.error_handler.os') + @mock.patch('certbot.error_handler.signal') def test_signal_handler(self, mock_signal, mock_os): # pylint: disable=protected-access mock_signal.getsignal.return_value = signal.SIG_DFL diff --git a/letsencrypt/tests/errors_test.py b/certbot/tests/errors_test.py similarity index 74% rename from letsencrypt/tests/errors_test.py rename to certbot/tests/errors_test.py index 5da7c0b7a..67611ed45 100644 --- a/letsencrypt/tests/errors_test.py +++ b/certbot/tests/errors_test.py @@ -1,19 +1,19 @@ -"""Tests for letsencrypt.errors.""" +"""Tests for certbot.errors.""" import unittest import mock from acme import messages -from letsencrypt import achallenges -from letsencrypt.tests import acme_util +from certbot import achallenges +from certbot.tests import acme_util class FaiiledChallengesTest(unittest.TestCase): - """Tests for letsencrypt.errors.FailedChallenges.""" + """Tests for certbot.errors.FailedChallenges.""" def setUp(self): - from letsencrypt.errors import FailedChallenges + from certbot.errors import FailedChallenges self.error = FailedChallenges(set([achallenges.DNS( domain="example.com", challb=messages.ChallengeBody( chall=acme_util.DNS, uri=None, @@ -25,10 +25,10 @@ class FaiiledChallengesTest(unittest.TestCase): class StandaloneBindErrorTest(unittest.TestCase): - """Tests for letsencrypt.errors.StandaloneBindError.""" + """Tests for certbot.errors.StandaloneBindError.""" def setUp(self): - from letsencrypt.errors import StandaloneBindError + from certbot.errors import StandaloneBindError self.error = StandaloneBindError(mock.sentinel.error, 1234) def test_instance_args(self): diff --git a/letsencrypt/tests/hook_test.py b/certbot/tests/hook_test.py similarity index 86% rename from letsencrypt/tests/hook_test.py rename to certbot/tests/hook_test.py index 3751133cf..ce78b5dc9 100644 --- a/letsencrypt/tests/hook_test.py +++ b/certbot/tests/hook_test.py @@ -6,8 +6,8 @@ import unittest import mock -from letsencrypt import errors -from letsencrypt import hooks +from certbot import errors +from certbot import hooks class HookTest(unittest.TestCase): def setUp(self): @@ -16,7 +16,7 @@ class HookTest(unittest.TestCase): def tearDown(self): pass - @mock.patch('letsencrypt.hooks._prog') + @mock.patch('certbot.hooks._prog') def test_validate_hooks(self, mock_prog): config = mock.MagicMock(pre_hook="", post_hook="ls -lR", renew_hook="uptime") hooks.validate_hooks(config) @@ -27,7 +27,7 @@ class HookTest(unittest.TestCase): config = mock.MagicMock(pre_hook="explodinator", post_hook="", renew_hook="") self.assertRaises(errors.HookCommandNotFound, hooks.validate_hooks, config) - @mock.patch('letsencrypt.hooks._is_exe') + @mock.patch('certbot.hooks._is_exe') def test_which(self, mock_is_exe): mock_is_exe.return_value = True self.assertEqual(hooks._which("/path/to/something"), "/path/to/something") @@ -39,7 +39,7 @@ class HookTest(unittest.TestCase): self.assertEqual(hooks._which("pingify"), None) self.assertEqual(hooks._which("/path/to/something"), None) - @mock.patch('letsencrypt.hooks._which') + @mock.patch('certbot.hooks._which') def test_prog(self, mockwhich): mockwhich.return_value = "/very/very/funky" self.assertEqual(hooks._prog("funky"), "funky") @@ -47,9 +47,9 @@ class HookTest(unittest.TestCase): self.assertEqual(hooks._prog("funky"), None) def _test_a_hook(self, config, hook_function, calls_expected): - with mock.patch('letsencrypt.hooks.logger') as mock_logger: + with mock.patch('certbot.hooks.logger') as mock_logger: mock_logger.warning = mock.MagicMock() - with mock.patch('letsencrypt.hooks._run_hook') as mock_run_hook: + with mock.patch('certbot.hooks._run_hook') as mock_run_hook: hook_function(config) hook_function(config) self.assertEqual(mock_run_hook.call_count, calls_expected) @@ -82,16 +82,16 @@ class HookTest(unittest.TestCase): mock_warn = self._test_a_hook(config, rhook, 0) self.assertEqual(mock_warn.call_count, 2) - @mock.patch('letsencrypt.hooks.Popen') + @mock.patch('certbot.hooks.Popen') def test_run_hook(self, mock_popen): - with mock.patch('letsencrypt.hooks.logger.error') as mock_error: + with mock.patch('certbot.hooks.logger.error') as mock_error: mock_cmd = mock.MagicMock() mock_cmd.returncode = 1 mock_cmd.communicate.return_value = ("", "") mock_popen.return_value = mock_cmd hooks._run_hook("ls") self.assertEqual(mock_error.call_count, 1) - with mock.patch('letsencrypt.hooks.logger.error') as mock_error: + with mock.patch('certbot.hooks.logger.error') as mock_error: mock_cmd.communicate.return_value = ("", "thing") hooks._run_hook("ls") self.assertEqual(mock_error.call_count, 2) diff --git a/letsencrypt/tests/le_util_test.py b/certbot/tests/le_util_test.py similarity index 83% rename from letsencrypt/tests/le_util_test.py rename to certbot/tests/le_util_test.py index 0f9464c6f..b6da4525f 100644 --- a/letsencrypt/tests/le_util_test.py +++ b/certbot/tests/le_util_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.le_util.""" +"""Tests for certbot.le_util.""" import argparse import errno import os @@ -10,17 +10,17 @@ import unittest import mock import six -from letsencrypt import errors +from certbot import errors class RunScriptTest(unittest.TestCase): - """Tests for letsencrypt.le_util.run_script.""" + """Tests for certbot.le_util.run_script.""" @classmethod def _call(cls, params): - from letsencrypt.le_util import run_script + from certbot.le_util import run_script return run_script(params) - @mock.patch("letsencrypt.le_util.subprocess.Popen") + @mock.patch("certbot.le_util.subprocess.Popen") def test_default(self, mock_popen): """These will be changed soon enough with reload.""" mock_popen().returncode = 0 @@ -30,13 +30,13 @@ class RunScriptTest(unittest.TestCase): self.assertEqual(out, "stdout") self.assertEqual(err, "stderr") - @mock.patch("letsencrypt.le_util.subprocess.Popen") + @mock.patch("certbot.le_util.subprocess.Popen") def test_bad_process(self, mock_popen): mock_popen.side_effect = OSError self.assertRaises(errors.SubprocessError, self._call, ["test"]) - @mock.patch("letsencrypt.le_util.subprocess.Popen") + @mock.patch("certbot.le_util.subprocess.Popen") def test_failure(self, mock_popen): mock_popen().communicate.return_value = ("", "") mock_popen().returncode = 1 @@ -45,29 +45,29 @@ class RunScriptTest(unittest.TestCase): class ExeExistsTest(unittest.TestCase): - """Tests for letsencrypt.le_util.exe_exists.""" + """Tests for certbot.le_util.exe_exists.""" @classmethod def _call(cls, exe): - from letsencrypt.le_util import exe_exists + from certbot.le_util import exe_exists return exe_exists(exe) - @mock.patch("letsencrypt.le_util.os.path.isfile") - @mock.patch("letsencrypt.le_util.os.access") + @mock.patch("certbot.le_util.os.path.isfile") + @mock.patch("certbot.le_util.os.access") def test_full_path(self, mock_access, mock_isfile): mock_access.return_value = True mock_isfile.return_value = True self.assertTrue(self._call("/path/to/exe")) - @mock.patch("letsencrypt.le_util.os.path.isfile") - @mock.patch("letsencrypt.le_util.os.access") + @mock.patch("certbot.le_util.os.path.isfile") + @mock.patch("certbot.le_util.os.access") def test_on_path(self, mock_access, mock_isfile): mock_access.return_value = True mock_isfile.return_value = True self.assertTrue(self._call("exe")) - @mock.patch("letsencrypt.le_util.os.path.isfile") - @mock.patch("letsencrypt.le_util.os.access") + @mock.patch("certbot.le_util.os.path.isfile") + @mock.patch("certbot.le_util.os.access") def test_not_found(self, mock_access, mock_isfile): mock_access.return_value = False mock_isfile.return_value = True @@ -75,7 +75,7 @@ class ExeExistsTest(unittest.TestCase): class MakeOrVerifyDirTest(unittest.TestCase): - """Tests for letsencrypt.le_util.make_or_verify_dir. + """Tests for certbot.le_util.make_or_verify_dir. Note that it is not possible to test for a wrong directory owner, as this testing script would have to be run as root. @@ -93,7 +93,7 @@ class MakeOrVerifyDirTest(unittest.TestCase): shutil.rmtree(self.root_path, ignore_errors=True) def _call(self, directory, mode): - from letsencrypt.le_util import make_or_verify_dir + from certbot.le_util import make_or_verify_dir return make_or_verify_dir(directory, mode, self.uid, strict=True) def test_creates_dir_when_missing(self): @@ -116,7 +116,7 @@ class MakeOrVerifyDirTest(unittest.TestCase): class CheckPermissionsTest(unittest.TestCase): - """Tests for letsencrypt.le_util.check_permissions. + """Tests for certbot.le_util.check_permissions. Note that it is not possible to test for a wrong file owner, as this testing script would have to be run as root. @@ -131,7 +131,7 @@ class CheckPermissionsTest(unittest.TestCase): os.remove(self.path) def _call(self, mode): - from letsencrypt.le_util import check_permissions + from certbot.le_util import check_permissions return check_permissions(self.path, mode, self.uid) def test_ok_mode(self): @@ -144,7 +144,7 @@ class CheckPermissionsTest(unittest.TestCase): class UniqueFileTest(unittest.TestCase): - """Tests for letsencrypt.le_util.unique_file.""" + """Tests for certbot.le_util.unique_file.""" def setUp(self): self.root_path = tempfile.mkdtemp() @@ -154,7 +154,7 @@ class UniqueFileTest(unittest.TestCase): shutil.rmtree(self.root_path, ignore_errors=True) def _call(self, mode=0o600): - from letsencrypt.le_util import unique_file + from certbot.le_util import unique_file return unique_file(self.default_name, mode) def test_returns_fd_for_writing(self): @@ -189,7 +189,7 @@ class UniqueFileTest(unittest.TestCase): class UniqueLineageNameTest(unittest.TestCase): - """Tests for letsencrypt.le_util.unique_lineage_name.""" + """Tests for certbot.le_util.unique_lineage_name.""" def setUp(self): self.root_path = tempfile.mkdtemp() @@ -198,7 +198,7 @@ class UniqueLineageNameTest(unittest.TestCase): shutil.rmtree(self.root_path, ignore_errors=True) def _call(self, filename, mode=0o777): - from letsencrypt.le_util import unique_lineage_name + from certbot.le_util import unique_lineage_name return unique_lineage_name(self.root_path, filename, mode) def test_basic(self): @@ -213,14 +213,14 @@ class UniqueLineageNameTest(unittest.TestCase): self.assertTrue(isinstance(name, str)) self.assertTrue("wow-0009.conf" in name) - @mock.patch("letsencrypt.le_util.os.fdopen") + @mock.patch("certbot.le_util.os.fdopen") def test_failure(self, mock_fdopen): err = OSError("whoops") err.errno = errno.EIO mock_fdopen.side_effect = err self.assertRaises(OSError, self._call, "wow") - @mock.patch("letsencrypt.le_util.os.fdopen") + @mock.patch("certbot.le_util.os.fdopen") def test_subsequent_failure(self, mock_fdopen): self._call("wow") err = OSError("whoops") @@ -230,7 +230,7 @@ class UniqueLineageNameTest(unittest.TestCase): class SafelyRemoveTest(unittest.TestCase): - """Tests for letsencrypt.le_util.safely_remove.""" + """Tests for certbot.le_util.safely_remove.""" def setUp(self): self.tmp = tempfile.mkdtemp() @@ -240,7 +240,7 @@ class SafelyRemoveTest(unittest.TestCase): shutil.rmtree(self.tmp) def _call(self): - from letsencrypt.le_util import safely_remove + from certbot.le_util import safely_remove return safely_remove(self.path) def test_exists(self): @@ -254,7 +254,7 @@ class SafelyRemoveTest(unittest.TestCase): # no error, yay! self.assertFalse(os.path.exists(self.path)) - @mock.patch("letsencrypt.le_util.os.remove") + @mock.patch("certbot.le_util.os.remove") def test_other_error_passthrough(self, mock_remove): mock_remove.side_effect = OSError self.assertRaises(OSError, self._call) @@ -264,12 +264,12 @@ class SafeEmailTest(unittest.TestCase): """Test safe_email.""" @classmethod def _call(cls, addr): - from letsencrypt.le_util import safe_email + from certbot.le_util import safe_email return safe_email(addr) def test_valid_emails(self): addrs = [ - "letsencrypt@letsencrypt.org", + "certbot@certbot.org", "tbd.ade@gmail.com", "abc_def.jdk@hotmail.museum", ] @@ -278,7 +278,7 @@ class SafeEmailTest(unittest.TestCase): def test_invalid_emails(self): addrs = [ - "letsencrypt@letsencrypt..org", + "certbot@certbot..org", ".tbd.ade@gmail.com", "~/abc_def.jdk@hotmail.museum", ] @@ -292,7 +292,7 @@ class AddDeprecatedArgumentTest(unittest.TestCase): self.parser = argparse.ArgumentParser() def _call(self, argument_name, nargs): - from letsencrypt.le_util import add_deprecated_argument + from certbot.le_util import add_deprecated_argument add_deprecated_argument(self.parser.add_argument, argument_name, nargs) @@ -308,14 +308,14 @@ class AddDeprecatedArgumentTest(unittest.TestCase): def _get_argparse_warnings(self, args): stderr = six.StringIO() - with mock.patch("letsencrypt.le_util.sys.stderr", new=stderr): + with mock.patch("certbot.le_util.sys.stderr", new=stderr): self.parser.parse_args(args) return stderr.getvalue() def test_help(self): self._call("--old-option", 2) stdout = six.StringIO() - with mock.patch("letsencrypt.le_util.sys.stdout", new=stdout): + with mock.patch("certbot.le_util.sys.stdout", new=stdout): try: self.parser.parse_args(["-h"]) except SystemExit: @@ -327,7 +327,7 @@ class EnforceDomainSanityTest(unittest.TestCase): """Test enforce_domain_sanity.""" def _call(self, domain): - from letsencrypt.le_util import enforce_domain_sanity + from certbot.le_util import enforce_domain_sanity return enforce_domain_sanity(domain) def test_nonascii_str(self): diff --git a/letsencrypt/tests/log_test.py b/certbot/tests/log_test.py similarity index 95% rename from letsencrypt/tests/log_test.py rename to certbot/tests/log_test.py index c1afd2c8a..a4f394870 100644 --- a/letsencrypt/tests/log_test.py +++ b/certbot/tests/log_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.log.""" +"""Tests for certbot.log.""" import logging import unittest @@ -10,7 +10,7 @@ class DialogHandlerTest(unittest.TestCase): def setUp(self): self.d = mock.MagicMock() - from letsencrypt.log import DialogHandler + from certbot.log import DialogHandler self.handler = DialogHandler(height=2, width=6, d=self.d) self.handler.PADDING_HEIGHT = 2 self.handler.PADDING_WIDTH = 4 diff --git a/letsencrypt/tests/notify_test.py b/certbot/tests/notify_test.py similarity index 79% rename from letsencrypt/tests/notify_test.py rename to certbot/tests/notify_test.py index 60364fff8..d2af5b001 100644 --- a/letsencrypt/tests/notify_test.py +++ b/certbot/tests/notify_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.notify.""" +"""Tests for certbot.notify.""" import socket import unittest @@ -8,9 +8,9 @@ import mock class NotifyTests(unittest.TestCase): """Tests for the notifier.""" - @mock.patch("letsencrypt.notify.smtplib.LMTP") + @mock.patch("certbot.notify.smtplib.LMTP") def test_smtp_success(self, mock_lmtp): - from letsencrypt.notify import notify + from certbot.notify import notify lmtp_obj = mock.MagicMock() mock_lmtp.return_value = lmtp_obj self.assertTrue(notify("Goose", "auntrhody@example.com", @@ -18,10 +18,10 @@ class NotifyTests(unittest.TestCase): self.assertEqual(lmtp_obj.connect.call_count, 1) self.assertEqual(lmtp_obj.sendmail.call_count, 1) - @mock.patch("letsencrypt.notify.smtplib.LMTP") - @mock.patch("letsencrypt.notify.subprocess.Popen") + @mock.patch("certbot.notify.smtplib.LMTP") + @mock.patch("certbot.notify.subprocess.Popen") def test_smtp_failure(self, mock_popen, mock_lmtp): - from letsencrypt.notify import notify + from certbot.notify import notify lmtp_obj = mock.MagicMock() mock_lmtp.return_value = lmtp_obj lmtp_obj.sendmail.side_effect = socket.error(17) @@ -32,10 +32,10 @@ class NotifyTests(unittest.TestCase): self.assertEqual(lmtp_obj.sendmail.call_count, 1) self.assertEqual(proc.communicate.call_count, 1) - @mock.patch("letsencrypt.notify.smtplib.LMTP") - @mock.patch("letsencrypt.notify.subprocess.Popen") + @mock.patch("certbot.notify.smtplib.LMTP") + @mock.patch("certbot.notify.subprocess.Popen") def test_everything_fails(self, mock_popen, mock_lmtp): - from letsencrypt.notify import notify + from certbot.notify import notify lmtp_obj = mock.MagicMock() mock_lmtp.return_value = lmtp_obj lmtp_obj.sendmail.side_effect = socket.error(17) diff --git a/letsencrypt/tests/reporter_test.py b/certbot/tests/reporter_test.py similarity index 95% rename from letsencrypt/tests/reporter_test.py rename to certbot/tests/reporter_test.py index 191c1b933..02c7981b7 100644 --- a/letsencrypt/tests/reporter_test.py +++ b/certbot/tests/reporter_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.reporter.""" +"""Tests for certbot.reporter.""" import mock import sys import unittest @@ -7,10 +7,10 @@ import six class ReporterTest(unittest.TestCase): - """Tests for letsencrypt.reporter.Reporter.""" + """Tests for certbot.reporter.Reporter.""" def setUp(self): - from letsencrypt import reporter + from certbot import reporter self.reporter = reporter.Reporter(mock.MagicMock(quiet=False)) self.old_stdout = sys.stdout diff --git a/letsencrypt/tests/reverter_test.py b/certbot/tests/reverter_test.py similarity index 94% rename from letsencrypt/tests/reverter_test.py rename to certbot/tests/reverter_test.py index aafd3b041..72ce3a121 100644 --- a/letsencrypt/tests/reverter_test.py +++ b/certbot/tests/reverter_test.py @@ -1,4 +1,4 @@ -"""Test letsencrypt.reverter.""" +"""Test certbot.reverter.""" import csv import itertools import logging @@ -9,14 +9,14 @@ import unittest import mock -from letsencrypt import errors +from certbot import errors class ReverterCheckpointLocalTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes, too-many-public-methods """Test the Reverter Class.""" def setUp(self): - from letsencrypt.reverter import Reverter + from certbot.reverter import Reverter # Disable spurious errors... we are trying to test for them logging.disable(logging.CRITICAL) @@ -50,7 +50,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): "{0}\n{1}\n".format(self.config1, self.config2)) def test_add_to_checkpoint_copy_failure(self): - with mock.patch("letsencrypt.reverter.shutil.copy2") as mock_copy2: + with mock.patch("certbot.reverter.shutil.copy2") as mock_copy2: mock_copy2.side_effect = IOError("bad copy") self.assertRaises( errors.ReverterError, self.reverter.add_to_checkpoint, @@ -116,7 +116,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_register_file_creation_write_error(self): m_open = mock.mock_open() - with mock.patch("letsencrypt.reverter.open", m_open, create=True): + with mock.patch("certbot.reverter.open", m_open, create=True): m_open.side_effect = OSError("bad open") self.assertRaises( errors.ReverterError, self.reverter.register_file_creation, @@ -144,13 +144,13 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_bad_register_undo_command(self): m_open = mock.mock_open() - with mock.patch("letsencrypt.reverter.open", m_open, create=True): + with mock.patch("certbot.reverter.open", m_open, create=True): m_open.side_effect = OSError("bad open") self.assertRaises( errors.ReverterError, self.reverter.register_undo_command, True, ["command"]) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_run_undo_commands(self, mock_run): mock_run.side_effect = ["", errors.SubprocessError] coms = [ @@ -200,7 +200,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_recover_checkpoint_copy_failure(self): self.reverter.add_to_temp_checkpoint(self.sets[0], "save1") - with mock.patch("letsencrypt.reverter.shutil.copy2") as mock_copy2: + with mock.patch("certbot.reverter.shutil.copy2") as mock_copy2: mock_copy2.side_effect = OSError("bad copy") self.assertRaises( errors.ReverterError, self.reverter.revert_temporary_config) @@ -208,19 +208,19 @@ class ReverterCheckpointLocalTest(unittest.TestCase): def test_recover_checkpoint_rm_failure(self): self.reverter.add_to_temp_checkpoint(self.sets[0], "temp save") - with mock.patch("letsencrypt.reverter.shutil.rmtree") as mock_rmtree: + with mock.patch("certbot.reverter.shutil.rmtree") as mock_rmtree: mock_rmtree.side_effect = OSError("Cannot remove tree") self.assertRaises( errors.ReverterError, self.reverter.revert_temporary_config) - @mock.patch("letsencrypt.reverter.logger.warning") + @mock.patch("certbot.reverter.logger.warning") def test_recover_checkpoint_missing_new_files(self, mock_warn): self.reverter.register_file_creation( True, os.path.join(self.dir1, "missing_file.txt")) self.reverter.revert_temporary_config() self.assertEqual(mock_warn.call_count, 1) - @mock.patch("letsencrypt.reverter.os.remove") + @mock.patch("certbot.reverter.os.remove") def test_recover_checkpoint_remove_failure(self, mock_remove): self.reverter.register_file_creation(True, self.config1) mock_remove.side_effect = OSError("Can't remove") @@ -265,7 +265,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): # pylint: disable=too-many-instance-attributes """Tests functions having to deal with full checkpoints.""" def setUp(self): - from letsencrypt.reverter import Reverter + from certbot.reverter import Reverter # Disable spurious errors... logging.disable(logging.CRITICAL) @@ -324,7 +324,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): # No need to warn for this... just make sure there are no errors. self.reverter.finalize_checkpoint("No checkpoint...") - @mock.patch("letsencrypt.reverter.shutil.move") + @mock.patch("certbot.reverter.shutil.move") def test_finalize_checkpoint_cannot_title(self, mock_move): self.reverter.add_to_checkpoint(self.sets[0], "perm save") mock_move.side_effect = OSError("cannot move") @@ -332,7 +332,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): self.assertRaises( errors.ReverterError, self.reverter.finalize_checkpoint, "Title") - @mock.patch("letsencrypt.reverter.os.rename") + @mock.patch("certbot.reverter.os.rename") def test_finalize_checkpoint_no_rename_directory(self, mock_rename): self.reverter.add_to_checkpoint(self.sets[0], "perm save") @@ -341,7 +341,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): self.assertRaises( errors.ReverterError, self.reverter.finalize_checkpoint, "Title") - @mock.patch("letsencrypt.reverter.logger") + @mock.patch("certbot.reverter.logger") def test_rollback_too_many(self, mock_logger): # Test no exist warning... self.reverter.rollback_checkpoints(1) @@ -361,7 +361,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): self.assertEqual(read_in(self.config2), "directive-dir2") self.assertFalse(os.path.isfile(config3)) - @mock.patch("letsencrypt.reverter.zope.component.getUtility") + @mock.patch("certbot.reverter.zope.component.getUtility") def test_view_config_changes(self, mock_output): """This is not strict as this is subject to change.""" self._setup_three_checkpoints() @@ -372,7 +372,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): # Make sure notification is output self.assertEqual(mock_output().notification.call_count, 1) - @mock.patch("letsencrypt.reverter.logger") + @mock.patch("certbot.reverter.logger") def test_view_config_changes_no_backups(self, mock_logger): self.reverter.view_config_changes() self.assertTrue(mock_logger.info.call_count > 0) @@ -426,7 +426,7 @@ class TestFullCheckpointsReverter(unittest.TestCase): def setup_work_direc(): """Setup directories. - :returns: Mocked :class:`letsencrypt.interfaces.IConfig` + :returns: Mocked :class:`certbot.interfaces.IConfig` """ work_dir = tempfile.mkdtemp("work") diff --git a/letsencrypt/tests/storage_test.py b/certbot/tests/storage_test.py similarity index 96% rename from letsencrypt/tests/storage_test.py rename to certbot/tests/storage_test.py index fcc481e8b..be626edc5 100644 --- a/letsencrypt/tests/storage_test.py +++ b/certbot/tests/storage_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt.storage.""" +"""Tests for certbot.storage.""" # pylint disable=protected-access import datetime import os @@ -10,11 +10,11 @@ import configobj import mock import pytz -from letsencrypt import configuration -from letsencrypt import errors -from letsencrypt.storage import ALL_FOUR +from certbot import configuration +from certbot import errors +from certbot.storage import ALL_FOUR -from letsencrypt.tests import test_util +from certbot.tests import test_util CERT = test_util.load_cert('cert.pem') @@ -41,7 +41,7 @@ class BaseRenewableCertTest(unittest.TestCase): """ def setUp(self): - from letsencrypt import storage + from certbot import storage self.tempdir = tempfile.mkdtemp() self.cli_config = configuration.RenewerConfiguration( @@ -76,7 +76,7 @@ class BaseRenewableCertTest(unittest.TestCase): self.defaults = configobj.ConfigObj() - with mock.patch("letsencrypt.storage.RenewableCert._check_symlinks") as check: + with mock.patch("certbot.storage.RenewableCert._check_symlinks") as check: check.return_value = True self.test_rc = storage.RenewableCert(config.filename, self.cli_config) @@ -99,7 +99,7 @@ class BaseRenewableCertTest(unittest.TestCase): class RenewableCertTests(BaseRenewableCertTest): # pylint: disable=too-many-public-methods - """Tests for letsencrypt.storage.""" + """Tests for certbot.storage.""" def test_initialization(self): self.assertEqual(self.test_rc.lineagename, "example.org") @@ -113,7 +113,7 @@ class RenewableCertTests(BaseRenewableCertTest): the renewal configuration file doesn't end in ".conf" """ - from letsencrypt import storage + from certbot import storage broken = os.path.join(self.tempdir, "broken.conf") with open(broken, "w") as f: f.write("[No closing bracket for you!") @@ -126,7 +126,7 @@ class RenewableCertTests(BaseRenewableCertTest): 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 + from certbot import storage config = configobj.ConfigObj() config["cert"] = "imaginary_cert.pem" # Here the required privkey is missing. @@ -327,7 +327,7 @@ class RenewableCertTests(BaseRenewableCertTest): real_unlink(path) self._write_out_ex_kinds() - with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink: + with mock.patch("certbot.storage.os.unlink") as mock_unlink: mock_unlink.side_effect = unlink_or_raise self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12) @@ -343,7 +343,7 @@ class RenewableCertTests(BaseRenewableCertTest): real_unlink(path) self._write_out_ex_kinds() - with mock.patch("letsencrypt.storage.os.unlink") as mock_unlink: + with mock.patch("certbot.storage.os.unlink") as mock_unlink: mock_unlink.side_effect = unlink_or_raise self.assertRaises(ValueError, self.test_rc.update_all_links_to, 12) @@ -394,7 +394,7 @@ class RenewableCertTests(BaseRenewableCertTest): os.unlink(self.test_rc.cert) self.assertRaises(errors.CertStorageError, self.test_rc.names) - @mock.patch("letsencrypt.storage.datetime") + @mock.patch("certbot.storage.datetime") def test_time_interval_judgments(self, mock_datetime): """Test should_autodeploy() and should_autorenew() on the basis of expiry time windows.""" @@ -474,7 +474,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.test_rc.configuration["autorenew"] = "0" self.assertFalse(self.test_rc.autorenewal_is_enabled()) - @mock.patch("letsencrypt.storage.RenewableCert.ocsp_revoked") + @mock.patch("certbot.storage.RenewableCert.ocsp_revoked") def test_should_autorenew(self, mock_ocsp): """Test should_autorenew on the basis of reasons other than expiry time window.""" @@ -494,7 +494,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertTrue(self.test_rc.should_autorenew()) mock_ocsp.return_value = False - @mock.patch("letsencrypt.storage.relevant_values") + @mock.patch("certbot.storage.relevant_values") def test_save_successor(self, mock_rv): # Mock relevant_values() to claim that all values are relevant here # (to avoid instantiating parser) @@ -563,33 +563,33 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10))) self.assertFalse(os.path.exists(temp_config_file)) - @mock.patch("letsencrypt.cli.helpful_parser") + @mock.patch("certbot.cli.helpful_parser") def test_relevant_values(self, mock_parser): """Test that relevant_values() can reject an irrelevant value.""" # pylint: disable=protected-access - from letsencrypt import storage + from certbot import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] mock_action = mock.Mock(dest="rsa_key_size", default=2048) mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"hello": "there"}), {}) - @mock.patch("letsencrypt.cli.helpful_parser") + @mock.patch("certbot.cli.helpful_parser") def test_relevant_values_default(self, mock_parser): """Test that relevant_values() can reject a default value.""" # pylint: disable=protected-access - from letsencrypt import storage + from certbot import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] mock_action = mock.Mock(dest="rsa_key_size", default=2048) mock_parser.parser._actions = [mock_action] self.assertEqual(storage.relevant_values({"rsa_key_size": 2048}), {}) - @mock.patch("letsencrypt.cli.helpful_parser") + @mock.patch("certbot.cli.helpful_parser") def test_relevant_values_nondefault(self, mock_parser): """Test that relevant_values() can retain a non-default value.""" # pylint: disable=protected-access - from letsencrypt import storage + from certbot import storage mock_parser.verb = "certonly" mock_parser.args = ["--standalone"] mock_action = mock.Mock(dest="rsa_key_size", default=2048) @@ -597,14 +597,14 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertEqual(storage.relevant_values({"rsa_key_size": 12}), {"rsa_key_size": 12}) - @mock.patch("letsencrypt.storage.relevant_values") + @mock.patch("certbot.storage.relevant_values") def test_new_lineage(self, mock_rv): """Test for new_lineage() class method.""" # Mock relevant_values to say everything is relevant here (so we # don't have to mock the parser to help it decide!) mock_rv.side_effect = lambda x: x - from letsencrypt import storage + from certbot import storage result = storage.RenewableCert.new_lineage( "the-lineage.com", "cert", "privkey", "chain", self.cli_config) # This consistency check tests most relevant properties about the @@ -637,14 +637,14 @@ class RenewableCertTests(BaseRenewableCertTest): # TODO: Conceivably we could test that the renewal parameters actually # got saved - @mock.patch("letsencrypt.storage.relevant_values") + @mock.patch("certbot.storage.relevant_values") def test_new_lineage_nonexistent_dirs(self, mock_rv): """Test that directories can be created if they don't exist.""" # Mock relevant_values to say everything is relevant here (so we # don't have to mock the parser to help it decide!) mock_rv.side_effect = lambda x: x - from letsencrypt import storage + from certbot import storage shutil.rmtree(self.cli_config.renewal_configs_dir) shutil.rmtree(self.cli_config.archive_dir) shutil.rmtree(self.cli_config.live_dir) @@ -659,9 +659,9 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertTrue(os.path.exists(os.path.join( self.cli_config.archive_dir, "the-lineage.com", "privkey1.pem"))) - @mock.patch("letsencrypt.storage.le_util.unique_lineage_name") + @mock.patch("certbot.storage.le_util.unique_lineage_name") def test_invalid_config_filename(self, mock_uln): - from letsencrypt import storage + from certbot import storage mock_uln.return_value = "this_does_not_end_with_dot_conf", "yikes" self.assertRaises(errors.CertStorageError, storage.RenewableCert.new_lineage, "example.com", @@ -691,7 +691,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.assertFalse(self.test_rc.ocsp_revoked()) def test_add_time_interval(self): - from letsencrypt import storage + from certbot import storage # 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)) @@ -733,7 +733,7 @@ class RenewableCertTests(BaseRenewableCertTest): excepted) def test_missing_cert(self): - from letsencrypt import storage + from certbot import storage self.assertRaises(errors.CertStorageError, storage.RenewableCert, self.config.filename, self.cli_config) @@ -755,7 +755,7 @@ class RenewableCertTests(BaseRenewableCertTest): for x in ALL_FOUR: target[x] = "somewhere" relevant_data = {"useful": "new_value"} - from letsencrypt import storage + from certbot import storage storage.write_renewal_config(temp, temp2, target, relevant_data) with open(temp2, "r") as f: content = f.read() diff --git a/letsencrypt/tests/test_util.py b/certbot/tests/test_util.py similarity index 100% rename from letsencrypt/tests/test_util.py rename to certbot/tests/test_util.py diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/cert1.pem b/certbot/tests/testdata/archive/sample-renewal/cert1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/cert1.pem rename to certbot/tests/testdata/archive/sample-renewal/cert1.pem diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/chain1.pem b/certbot/tests/testdata/archive/sample-renewal/chain1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/chain1.pem rename to certbot/tests/testdata/archive/sample-renewal/chain1.pem diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/fullchain1.pem b/certbot/tests/testdata/archive/sample-renewal/fullchain1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/fullchain1.pem rename to certbot/tests/testdata/archive/sample-renewal/fullchain1.pem diff --git a/letsencrypt/tests/testdata/archive/sample-renewal/privkey1.pem b/certbot/tests/testdata/archive/sample-renewal/privkey1.pem similarity index 100% rename from letsencrypt/tests/testdata/archive/sample-renewal/privkey1.pem rename to certbot/tests/testdata/archive/sample-renewal/privkey1.pem diff --git a/letsencrypt/tests/testdata/cert-san.pem b/certbot/tests/testdata/cert-san.pem similarity index 100% rename from letsencrypt/tests/testdata/cert-san.pem rename to certbot/tests/testdata/cert-san.pem diff --git a/letsencrypt/tests/testdata/cert.b64jose b/certbot/tests/testdata/cert.b64jose similarity index 100% rename from letsencrypt/tests/testdata/cert.b64jose rename to certbot/tests/testdata/cert.b64jose diff --git a/letsencrypt/tests/testdata/cert.der b/certbot/tests/testdata/cert.der similarity index 100% rename from letsencrypt/tests/testdata/cert.der rename to certbot/tests/testdata/cert.der diff --git a/letsencrypt/tests/testdata/cert.pem b/certbot/tests/testdata/cert.pem similarity index 100% rename from letsencrypt/tests/testdata/cert.pem rename to certbot/tests/testdata/cert.pem diff --git a/letsencrypt/tests/testdata/cli.ini b/certbot/tests/testdata/cli.ini similarity index 100% rename from letsencrypt/tests/testdata/cli.ini rename to certbot/tests/testdata/cli.ini diff --git a/letsencrypt/tests/testdata/csr-6sans.pem b/certbot/tests/testdata/csr-6sans.pem similarity index 100% rename from letsencrypt/tests/testdata/csr-6sans.pem rename to certbot/tests/testdata/csr-6sans.pem diff --git a/letsencrypt/tests/testdata/csr-nosans.pem b/certbot/tests/testdata/csr-nosans.pem similarity index 100% rename from letsencrypt/tests/testdata/csr-nosans.pem rename to certbot/tests/testdata/csr-nosans.pem diff --git a/letsencrypt/tests/testdata/csr-san.der b/certbot/tests/testdata/csr-san.der similarity index 100% rename from letsencrypt/tests/testdata/csr-san.der rename to certbot/tests/testdata/csr-san.der diff --git a/letsencrypt/tests/testdata/csr-san.pem b/certbot/tests/testdata/csr-san.pem similarity index 100% rename from letsencrypt/tests/testdata/csr-san.pem rename to certbot/tests/testdata/csr-san.pem diff --git a/letsencrypt/tests/testdata/csr.der b/certbot/tests/testdata/csr.der similarity index 100% rename from letsencrypt/tests/testdata/csr.der rename to certbot/tests/testdata/csr.der diff --git a/letsencrypt/tests/testdata/csr.pem b/certbot/tests/testdata/csr.pem similarity index 100% rename from letsencrypt/tests/testdata/csr.pem rename to certbot/tests/testdata/csr.pem diff --git a/letsencrypt/tests/testdata/dsa512_key.pem b/certbot/tests/testdata/dsa512_key.pem similarity index 100% rename from letsencrypt/tests/testdata/dsa512_key.pem rename to certbot/tests/testdata/dsa512_key.pem diff --git a/letsencrypt/tests/testdata/dsa_cert.pem b/certbot/tests/testdata/dsa_cert.pem similarity index 100% rename from letsencrypt/tests/testdata/dsa_cert.pem rename to certbot/tests/testdata/dsa_cert.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/cert.pem b/certbot/tests/testdata/live/sample-renewal/cert.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/cert.pem rename to certbot/tests/testdata/live/sample-renewal/cert.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/chain.pem b/certbot/tests/testdata/live/sample-renewal/chain.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/chain.pem rename to certbot/tests/testdata/live/sample-renewal/chain.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/fullchain.pem b/certbot/tests/testdata/live/sample-renewal/fullchain.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/fullchain.pem rename to certbot/tests/testdata/live/sample-renewal/fullchain.pem diff --git a/letsencrypt/tests/testdata/live/sample-renewal/privkey.pem b/certbot/tests/testdata/live/sample-renewal/privkey.pem similarity index 100% rename from letsencrypt/tests/testdata/live/sample-renewal/privkey.pem rename to certbot/tests/testdata/live/sample-renewal/privkey.pem diff --git a/letsencrypt/tests/testdata/matching_cert.pem b/certbot/tests/testdata/matching_cert.pem similarity index 100% rename from letsencrypt/tests/testdata/matching_cert.pem rename to certbot/tests/testdata/matching_cert.pem diff --git a/letsencrypt/tests/testdata/rsa256_key.pem b/certbot/tests/testdata/rsa256_key.pem similarity index 100% rename from letsencrypt/tests/testdata/rsa256_key.pem rename to certbot/tests/testdata/rsa256_key.pem diff --git a/letsencrypt/tests/testdata/rsa512_key.pem b/certbot/tests/testdata/rsa512_key.pem similarity index 100% rename from letsencrypt/tests/testdata/rsa512_key.pem rename to certbot/tests/testdata/rsa512_key.pem diff --git a/letsencrypt/tests/testdata/rsa512_key_2.pem b/certbot/tests/testdata/rsa512_key_2.pem similarity index 100% rename from letsencrypt/tests/testdata/rsa512_key_2.pem rename to certbot/tests/testdata/rsa512_key_2.pem diff --git a/letsencrypt/tests/testdata/sample-renewal-ancient.conf b/certbot/tests/testdata/sample-renewal-ancient.conf similarity index 100% rename from letsencrypt/tests/testdata/sample-renewal-ancient.conf rename to certbot/tests/testdata/sample-renewal-ancient.conf diff --git a/letsencrypt/tests/testdata/sample-renewal.conf b/certbot/tests/testdata/sample-renewal.conf similarity index 100% rename from letsencrypt/tests/testdata/sample-renewal.conf rename to certbot/tests/testdata/sample-renewal.conf diff --git a/letsencrypt/tests/testdata/webrootconftest.ini b/certbot/tests/testdata/webrootconftest.ini similarity index 100% rename from letsencrypt/tests/testdata/webrootconftest.ini rename to certbot/tests/testdata/webrootconftest.ini diff --git a/pep8.travis.sh b/pep8.travis.sh index 91124bdbd..fe8f84639 100755 --- a/pep8.travis.sh +++ b/pep8.travis.sh @@ -7,7 +7,7 @@ pep8 --config=acme/.pep8 acme pep8 \ setup.py \ - letsencrypt \ + certbot \ letsencrypt-apache \ letsencrypt-nginx \ letsencrypt-compatibility-test \ diff --git a/setup.py b/setup.py index 87cef2cb2..022f3ffb3 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read_file(filename, encoding='utf8'): here = os.path.abspath(os.path.dirname(__file__)) # read version number (and other metadata) from package init -init_fn = os.path.join(here, 'letsencrypt', '__init__.py') +init_fn = os.path.join(here, 'certbot', '__init__.py') meta = dict(re.findall(r"""__([a-z]+)__ = '([^']+)""", read_file(init_fn))) readme = read_file(os.path.join(here, 'README.rst')) @@ -85,7 +85,7 @@ docs_extras = [ ] setup( - name='letsencrypt', + name='certbot', version=version, description="Let's Encrypt client", long_description=readme, # later: + '\n\n' + changes @@ -122,18 +122,18 @@ setup( }, # to test all packages run "python setup.py test -s - # {acme,letsencrypt_apache,letsencrypt_nginx}" - test_suite='letsencrypt', + # {acme,certbot_apache,certbot_nginx}" + test_suite='certbot', entry_points={ 'console_scripts': [ - 'letsencrypt = letsencrypt.main:main', + 'certbot = certbot.main:main', ], - 'letsencrypt.plugins': [ - 'manual = letsencrypt.plugins.manual:Authenticator', - 'null = letsencrypt.plugins.null:Installer', - 'standalone = letsencrypt.plugins.standalone:Authenticator', - 'webroot = letsencrypt.plugins.webroot:Authenticator', + 'certbot.plugins': [ + 'manual = certbot.plugins.manual:Authenticator', + 'null = certbot.plugins.null:Installer', + 'standalone = certbot.plugins.standalone:Authenticator', + 'webroot = certbot.plugins.webroot:Authenticator', ], }, ) diff --git a/tox.cover.sh b/tox.cover.sh index 8418de9a8..7097623be 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -9,13 +9,13 @@ # -e makes sure we fail fast and don't submit coveralls submit if [ "xxx$1" = "xxx" ]; then - pkgs="letsencrypt acme letsencrypt_apache letsencrypt_nginx letshelp_letsencrypt" + pkgs="certbot acme letsencrypt_apache letsencrypt_nginx letshelp_letsencrypt" else pkgs="$@" fi cover () { - if [ "$1" = "letsencrypt" ]; then + if [ "$1" = "certbot" ]; then min=98 elif [ "$1" = "acme" ]; then min=100 diff --git a/tox.ini b/tox.ini index 5768733b5..8f16b71d1 100644 --- a/tox.ini +++ b/tox.ini @@ -3,8 +3,6 @@ # "tox" from this directory. [tox] -# acme and letsencrypt are not yet on pypi, so when Tox invokes -# "install *.zip", it will not find deps skipsdist = true envlist = py{26,27,33,34,35},py{26,27}-oldest,cover,lint @@ -18,7 +16,7 @@ commands = pip install -e acme[dev] nosetests -v acme pip install -e .[dev] - nosetests -v letsencrypt + nosetests -v certbot pip install -e letsencrypt-apache nosetests -v letsencrypt_apache pip install -e letsencrypt-nginx @@ -68,7 +66,7 @@ basepython = python2.7 commands = pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt ./pep8.travis.sh - pylint --rcfile=.pylintrc letsencrypt + pylint --rcfile=.pylintrc certbot pylint --rcfile=acme/.pylintrc acme/acme pylint --rcfile=.pylintrc letsencrypt-apache/letsencrypt_apache pylint --rcfile=.pylintrc letsencrypt-nginx/letsencrypt_nginx From 1b565ef74af9594ec6e6c58d84b2cc4519e5d747 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:13:50 -0700 Subject: [PATCH 210/317] s/Let's Encrypt/Certbot --- certbot/__init__.py | 2 +- certbot/account.py | 6 +++--- certbot/auth_handler.py | 4 ++-- certbot/cli.py | 10 +++++----- certbot/client.py | 4 ++-- certbot/configuration.py | 2 +- certbot/constants.py | 2 +- certbot/crypto_util.py | 2 +- certbot/display/__init__.py | 2 +- certbot/display/enhancements.py | 2 +- certbot/display/util.py | 2 +- certbot/errors.py | 14 +++++++------- certbot/interfaces.py | 10 +++++----- certbot/le_util.py | 2 +- certbot/main.py | 8 ++++---- certbot/notify.py | 2 +- certbot/plugins/__init__.py | 2 +- certbot/plugins/selection.py | 2 +- certbot/reverter.py | 5 ++--- certbot/storage.py | 6 +++--- certbot/tests/__init__.py | 2 +- certbot/tests/display/__init__.py | 2 +- certbot/tests/reverter_test.py | 2 +- 23 files changed, 47 insertions(+), 48 deletions(-) diff --git a/certbot/__init__.py b/certbot/__init__.py index 3f65e6d83..a48d62548 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client.""" +"""Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 __version__ = '0.6.0.dev0' diff --git a/certbot/account.py b/certbot/account.py index 8c1d55177..cc50a6ea6 100644 --- a/certbot/account.py +++ b/certbot/account.py @@ -81,15 +81,15 @@ class Account(object): # pylint: disable=too-few-public-methods def report_new_account(acc, config): - """Informs the user about their new Let's Encrypt account.""" + """Informs the user about their new ACME account.""" reporter = zope.component.queryUtility(interfaces.IReporter) if reporter is None: return reporter.add_message( - "Your account credentials have been saved in your Let's Encrypt " + "Your account credentials have been saved in your Certbot " "configuration directory at {0}. You should make a secure backup " "of this folder now. This configuration directory will also " - "contain certificates and private keys obtained by Let's Encrypt " + "contain certificates and private keys obtained by Certbot " "so making regular backups of this folder is ideal.".format( config.config_dir), reporter.MEDIUM_PRIORITY) diff --git a/certbot/auth_handler.py b/certbot/auth_handler.py index 377747772..f5557d604 100644 --- a/certbot/auth_handler.py +++ b/certbot/auth_handler.py @@ -445,7 +445,7 @@ _ERROR_HELP = { "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 Certbot " "again.", "serverInternal": "Unfortunately, an error on the ACME server prevented you from completing " @@ -453,7 +453,7 @@ _ERROR_HELP = { "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.", + "with the Certbot client.", "unauthorized": _ERROR_HELP_COMMON, "unknownHost": _ERROR_HELP_COMMON, } diff --git a/certbot/cli.py b/certbot/cli.py index ebb9a6ca2..e920ab358 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -1,4 +1,4 @@ -"""Let's Encrypt command line argument & config processing.""" +"""Certbot command line argument & config processing.""" from __future__ import print_function import argparse import glob @@ -48,9 +48,9 @@ cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "certbot" SHORT_USAGE = """ {0} [SUBCOMMAND] [options] [-d domain] [-d domain] ... -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. Major SUBCOMMANDS are: +Certbot can obtain and install HTTPS/TLS/SSL certificates. By 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 Obtain cert, but do not install it (aka "auth") @@ -848,7 +848,7 @@ def _paths_parser(helpful): def _plugins_parsing(helpful, plugins): helpful.add_group( - "plugins", description="Let's Encrypt client supports an " + "plugins", description="Certbot client supports an " "extensible plugins architecture. See '%(prog)s plugins' for a " "list of all installed plugins and their names. You can force " "a particular plugin by setting options provided below. Running " diff --git a/certbot/client.py b/certbot/client.py index 1ca301c1e..71d753e42 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client API.""" +"""Certbot client API.""" import logging import os @@ -360,7 +360,7 @@ class Client(object): fullchain_path=fullchain_path) self.installer.save() # needed by the Apache plugin - self.installer.save("Deployed Let's Encrypt Certificate") + self.installer.save("Deployed ACME Certificate") msg = ("We were unable to install your certificate, " "however, we successfully restored your " diff --git a/certbot/configuration.py b/certbot/configuration.py index e38ff2cfa..172b35bfe 100644 --- a/certbot/configuration.py +++ b/certbot/configuration.py @@ -1,4 +1,4 @@ -"""Let's Encrypt user-supplied configuration.""" +"""Certbot user-supplied configuration.""" import copy import os diff --git a/certbot/constants.py b/certbot/constants.py index 09df720b9..ef59b8769 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -1,4 +1,4 @@ -"""Let's Encrypt constants.""" +"""Certbot constants.""" import os import logging diff --git a/certbot/crypto_util.py b/certbot/crypto_util.py index 1b4907bfa..b699ce653 100644 --- a/certbot/crypto_util.py +++ b/certbot/crypto_util.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client crypto utility functions. +"""Certbot client crypto utility functions. .. todo:: Make the transition to use PSS rather than PKCS1_v1_5 when the server is capable of handling the signatures. diff --git a/certbot/display/__init__.py b/certbot/display/__init__.py index 01e3ca11f..9d39dce92 100644 --- a/certbot/display/__init__.py +++ b/certbot/display/__init__.py @@ -1 +1 @@ -"""Let's Encrypt display utilities.""" +"""Certbot display utilities.""" diff --git a/certbot/display/enhancements.py b/certbot/display/enhancements.py index e7432a91e..3b128a874 100644 --- a/certbot/display/enhancements.py +++ b/certbot/display/enhancements.py @@ -1,4 +1,4 @@ -"""Let's Encrypt Enhancement Display""" +"""Certbot Enhancement Display""" import logging import zope.component diff --git a/certbot/display/util.py b/certbot/display/util.py index f01c4bc12..8de607534 100644 --- a/certbot/display/util.py +++ b/certbot/display/util.py @@ -1,4 +1,4 @@ -"""Let's Encrypt display.""" +"""Certbot display.""" import os import textwrap diff --git a/certbot/errors.py b/certbot/errors.py index 532a3a545..1553b6317 100644 --- a/certbot/errors.py +++ b/certbot/errors.py @@ -1,8 +1,8 @@ -"""Let's Encrypt client errors.""" +"""Certbot client errors.""" class Error(Exception): - """Generic Let's Encrypt client error.""" + """Generic Certbot client error.""" class AccountStorageError(Error): @@ -14,7 +14,7 @@ class AccountNotFound(AccountStorageError): class ReverterError(Error): - """Let's Encrypt Reverter error.""" + """Certbot Reverter error.""" class SubprocessError(Error): @@ -54,7 +54,7 @@ class FailedChallenges(AuthorizationError): # Plugin Errors class PluginError(Error): - """Let's Encrypt Plugin error.""" + """Certbot Plugin error.""" class PluginEnhancementAlreadyPresent(Error): @@ -66,15 +66,15 @@ class PluginSelectionError(Error): class NoInstallationError(PluginError): - """Let's Encrypt No Installation error.""" + """Certbot No Installation error.""" class MisconfigurationError(PluginError): - """Let's Encrypt Misconfiguration error.""" + """Certbot Misconfiguration error.""" class NotSupportedError(PluginError): - """Let's Encrypt Plugin function not supported error.""" + """Certbot Plugin function not supported error.""" class StandaloneBindError(Error): diff --git a/certbot/interfaces.py b/certbot/interfaces.py index 3eeb6a6f5..d65f5cf01 100644 --- a/certbot/interfaces.py +++ b/certbot/interfaces.py @@ -1,4 +1,4 @@ -"""Let's Encrypt client interfaces.""" +"""Certbot client interfaces.""" import abc import zope.interface @@ -97,7 +97,7 @@ class IPluginFactory(zope.interface.Interface): class IPlugin(zope.interface.Interface): - """Let's Encrypt plugin.""" + """Certbot plugin.""" def prepare(): """Prepare the plugin. @@ -130,7 +130,7 @@ class IPlugin(zope.interface.Interface): class IAuthenticator(IPlugin): - """Generic Let's Encrypt Authenticator. + """Generic Certbot Authenticator. Class represents all possible tools processes that have the ability to perform challenges and attain a certificate. @@ -190,7 +190,7 @@ class IAuthenticator(IPlugin): class IConfig(zope.interface.Interface): - """Let's Encrypt user-supplied configuration. + """Certbot user-supplied configuration. .. warning:: The values stored in the configuration have not been filtered, stripped or sanitized. @@ -230,7 +230,7 @@ class IConfig(zope.interface.Interface): class IInstaller(IPlugin): - """Generic Let's Encrypt Installer Interface. + """Generic Certbot Installer Interface. Represents any server that an X509 certificate can be placed. diff --git a/certbot/le_util.py b/certbot/le_util.py index b9545b2bc..f5148b949 100644 --- a/certbot/le_util.py +++ b/certbot/le_util.py @@ -1,4 +1,4 @@ -"""Utilities for all Let's Encrypt.""" +"""Utilities for all Certbot.""" import argparse import collections import errno diff --git a/certbot/main.py b/certbot/main.py index c00ad8b59..72f4fe66e 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -1,4 +1,4 @@ -"""Let's Encrypt main entry point.""" +"""Certbot main entry point.""" from __future__ import print_function import atexit import functools @@ -38,14 +38,14 @@ logger = logging.getLogger(__name__) def _suggest_donation_if_appropriate(config, action): - """Potentially suggest a donation to support Let's Encrypt.""" + """Potentially suggest a donation to support Certbot.""" if config.staging or config.verb == "renew": # --dry-run implies --staging return if action not in ["renew", "newcert"]: return reporter_util = zope.component.getUtility(interfaces.IReporter) - msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n" + msg = ("If you like Certbot, please consider supporting our work by:\n\n" "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" "Donating to EFF: https://eff.org/donate-le\n\n") reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) @@ -289,7 +289,7 @@ def _report_new_cert(cert_path, fullchain_path): # and say something more informative here. 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." + "certificate in the future, simply run Certbot again." .format(and_chain, path, expiry)) reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) diff --git a/certbot/notify.py b/certbot/notify.py index cfbfa82b0..dda0a85af 100644 --- a/certbot/notify.py +++ b/certbot/notify.py @@ -14,7 +14,7 @@ def notify(subject, whom, what): """ msg = email.message_from_string(what) - msg.add_header("From", "Let's Encrypt renewal agent ") + msg.add_header("From", "Certbot renewal agent ") msg.add_header("To", whom) msg.add_header("Subject", subject) msg = msg.as_string() diff --git a/certbot/plugins/__init__.py b/certbot/plugins/__init__.py index 538189015..7b1aca2b4 100644 --- a/certbot/plugins/__init__.py +++ b/certbot/plugins/__init__.py @@ -1 +1 @@ -"""Let's Encrypt client.plugins.""" +"""Certbot client.plugins.""" diff --git a/certbot/plugins/selection.py b/certbot/plugins/selection.py index 0febeb82c..ac509d779 100644 --- a/certbot/plugins/selection.py +++ b/certbot/plugins/selection.py @@ -33,7 +33,7 @@ def pick_installer(config, default, plugins, def pick_authenticator( config, default, plugins, question="How would you " - "like to authenticate with the Let's Encrypt CA?"): + "like to authenticate with the ACME CA?"): """Pick authentication plugin.""" return pick_plugin( config, default, plugins, question, (interfaces.IAuthenticator,)) diff --git a/certbot/reverter.py b/certbot/reverter.py index 6017ef602..fe6d9f24f 100644 --- a/certbot/reverter.py +++ b/certbot/reverter.py @@ -80,7 +80,7 @@ class Reverter(object): if not backups: logger.warning( - "Let's Encrypt hasn't modified your configuration, so rollback " + "Certbot hasn't modified your configuration, so rollback " "isn't available.") elif len(backups) < rollback: logger.warning("Unable to rollback %d checkpoints, only %d exist", @@ -112,8 +112,7 @@ class Reverter(object): if num: backups = backups[:num] if not backups: - logger.info("The Let's Encrypt client has not saved any backups " - "of your configuration") + logger.info("Certbot has not saved backups of your configuration") return # Make sure there isn't anything unexpected in the backup folder diff --git a/certbot/storage.py b/certbot/storage.py index 1a6dee892..4ef614a8e 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -186,9 +186,9 @@ def relevant_values(all_values): class RenewableCert(object): # pylint: disable=too-many-instance-attributes """Renewable certificate. - Represents a lineage of certificates that is under the management - of the Let's Encrypt client, indicated by the existence of an - associated renewal configuration file. + Represents a lineage of certificates that is under the management of + Certbot, indicated by the existence of an associated renewal + configuration file. Note that the notion of "current version" for a lineage is maintained on disk in the structure of symbolic links, and is not diff --git a/certbot/tests/__init__.py b/certbot/tests/__init__.py index d9db68022..2f4d6e07c 100644 --- a/certbot/tests/__init__.py +++ b/certbot/tests/__init__.py @@ -1 +1 @@ -"""Let's Encrypt Tests""" +"""Certbot Tests""" diff --git a/certbot/tests/display/__init__.py b/certbot/tests/display/__init__.py index 79a386ea2..ec5354e57 100644 --- a/certbot/tests/display/__init__.py +++ b/certbot/tests/display/__init__.py @@ -1 +1 @@ -"""Let's Encrypt Display Tests""" +"""Certbot Display Tests""" diff --git a/certbot/tests/reverter_test.py b/certbot/tests/reverter_test.py index 72ce3a121..eda5ffb36 100644 --- a/certbot/tests/reverter_test.py +++ b/certbot/tests/reverter_test.py @@ -96,7 +96,7 @@ class ReverterCheckpointLocalTest(unittest.TestCase): self.reverter.register_file_creation(True, self.config2) self.reverter.register_file_creation(True, config3, config4) - # Simulate Let's Encrypt crash... recovery routine is run + # Simulate Certbot crash... recovery routine is run self.reverter.recovery_routine() self.assertFalse(os.path.isfile(self.config1)) From 2002511f8146db70cfc148c4f39121cfd9d8d0bd Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:30:57 -0700 Subject: [PATCH 211/317] s/letsencrypt/certbot letsencrypt-apache --- .../LICENSE.txt | 0 certbot-apache/MANIFEST.in | 7 + .../README.rst | 0 .../certbot_apache}/__init__.py | 0 .../certbot_apache}/augeas_configurator.py | 12 +- .../certbot_apache}/augeas_lens/README | 0 .../certbot_apache}/augeas_lens/httpd.aug | 0 .../centos-options-ssl-apache.conf | 0 .../certbot_apache}/configurator.py | 90 ++++++------ .../certbot_apache}/constants.py | 12 +- .../certbot_apache}/display_ops.py | 8 +- .../certbot_apache}/obj.py | 2 +- .../certbot_apache}/options-ssl-apache.conf | 0 .../certbot_apache}/parser.py | 4 +- .../certbot_apache}/tests/__init__.py | 0 .../tests/apache-conf-files/NEEDED.txt | 6 + .../tests/apache-conf-files/apache-conf-test | 2 +- .../failing/missing-double-quote-1724.conf | 0 .../failing/multivhost-1093.conf | 0 .../failing/multivhost-1093b.conf | 0 .../apache-conf-files/passing/1626-1531.conf | 0 .../apache-conf-files/passing/README.modules | 0 .../passing/anarcat-1531.conf | 0 .../drupal-errordocument-arg-1724.conf | 0 .../passing/drupal-htaccess-1531.conf | 0 .../passing/example-1755.conf | 0 .../passing/example-ssl.conf | 0 .../apache-conf-files/passing/example.conf | 0 .../passing/finalize-1243.apache2.conf.txt | 0 .../passing/finalize-1243.conf | 2 +- .../passing/graphite-quote-1934.conf | 0 .../apache-conf-files/passing/ipv6-1143.conf | 0 .../apache-conf-files/passing/ipv6-1143b.conf | 0 .../apache-conf-files/passing/ipv6-1143c.conf | 0 .../apache-conf-files/passing/ipv6-1143d.conf | 0 .../passing/missing-quote-1724.conf | 0 .../passing/modmacro-1385.conf | 0 .../passing/owncloud-1264.conf | 0 .../passing/rewrite-quote-1960.conf | 0 .../passing/roundcube-1222.conf | 0 .../passing/section-continuations-2525.conf | 0 .../passing/semacode-1598.conf | 0 .../passing/sslrequire-wordlist-1827.htaccess | 0 .../passing/two-blocks-one-line-1693.conf | 0 .../tests/augeas_configurator_test.py | 6 +- .../tests/complex_parsing_test.py | 8 +- .../tests/configurator_test.py | 130 +++++++++--------- .../certbot_apache}/tests/constants_test.py | 10 +- .../certbot_apache}/tests/display_ops_test.py | 28 ++-- .../certbot_apache}/tests/obj_test.py | 16 +-- .../certbot_apache}/tests/parser_test.py | 44 +++--- .../testdata/complex_parsing/apache2.conf | 0 .../complex_parsing/conf-enabled/dummy.conf | 0 .../complex_parsing/test_fnmatch.conf | 0 .../complex_parsing/test_variables.conf | 0 .../default_vhost/apache2/apache2.conf | 0 .../other-vhosts-access-log.conf | 0 .../apache2/conf-available/security.conf | 0 .../apache2/conf-available/serve-cgi-bin.conf | 0 .../conf-enabled/other-vhosts-access-log.conf | 0 .../apache2/conf-enabled/security.conf | 0 .../apache2/conf-enabled/serve-cgi-bin.conf | 0 .../default_vhost/apache2/envvars | 0 .../apache2/mods-available/ssl.conf | 0 .../apache2/mods-available/ssl.load | 0 .../default_vhost/apache2/ports.conf | 0 .../apache2/sites-available/000-default.conf | 0 .../apache2/sites-available/default-ssl.conf | 4 +- .../apache2/sites-enabled/000-default.conf | 0 .../debian_apache_2_4/default_vhost/sites | 0 .../multiple_vhosts/apache2/apache2.conf | 0 .../apache2/conf-available/bad_conf_file.conf | 0 .../other-vhosts-access-log.conf | 0 .../apache2/conf-available/security.conf | 0 .../apache2/conf-available/serve-cgi-bin.conf | 0 .../conf-enabled/other-vhosts-access-log.conf | 0 .../apache2/conf-enabled/security.conf | 0 .../apache2/conf-enabled/serve-cgi-bin.conf | 0 .../multiple_vhosts/apache2/envvars | 0 .../apache2/mods-available/authz_svn.load | 0 .../apache2/mods-available/dav.load | 0 .../apache2/mods-available/dav_svn.conf | 0 .../apache2/mods-available/dav_svn.load | 0 .../apache2/mods-available/rewrite.load | 0 .../apache2/mods-available/ssl.conf | 0 .../apache2/mods-available/ssl.load | 0 .../apache2/mods-enabled}/.gitignore | 0 .../apache2/mods-enabled/authz_svn.load | 0 .../apache2/mods-enabled/dav.load | 0 .../apache2/mods-enabled/dav_svn.conf | 0 .../apache2/mods-enabled/dav_svn.load | 0 .../multiple_vhosts/apache2/ports.conf | 0 .../apache2/sites-available/000-default.conf | 0 .../apache2/sites-available/certbot.conf | 4 +- .../default-ssl-port-only.conf | 4 +- .../apache2/sites-available/default-ssl.conf | 4 +- .../sites-available/encryption-example.conf | 0 .../sites-available/mod_macro-example.conf | 0 .../apache2/sites-available/wildcard.conf | 0 .../apache2/sites-enabled/000-default.conf | 0 .../apache2/sites-enabled/certbot.conf | 1 + .../sites-enabled/encryption-example.conf | 0 .../sites-enabled/mod_macro-example.conf | 0 .../debian_apache_2_4/multiple_vhosts/sites | 2 +- .../certbot_apache}/tests/tls_sni_01_test.py | 16 +-- .../certbot_apache}/tests/util.py | 32 ++--- .../certbot_apache}/tls_sni_01.py | 6 +- .../docs/.gitignore | 0 .../docs/Makefile | 8 +- .../docs/_static}/.gitignore | 0 .../docs/_templates}/.gitignore | 0 .../docs/api.rst | 0 .../docs/api/augeas_configurator.rst | 5 + certbot-apache/docs/api/configurator.rst | 5 + certbot-apache/docs/api/display_ops.rst | 5 + certbot-apache/docs/api/obj.rst | 5 + certbot-apache/docs/api/parser.rst | 5 + certbot-apache/docs/api/tls_sni_01.rst | 5 + .../docs/conf.py | 16 +-- .../docs/index.rst | 6 +- .../docs/make.bat | 4 +- .../readthedocs.org.requirements.txt | 2 +- .../setup.py | 10 +- letsencrypt-apache/MANIFEST.in | 7 - .../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/obj.rst | 5 - letsencrypt-apache/docs/api/parser.rst | 5 - letsencrypt-apache/docs/api/tls_sni_01.rst | 5 - .../tests/apache-conf-files/NEEDED.txt | 6 - .../apache2/sites-enabled/letsencrypt.conf | 1 - tox.ini | 14 +- 133 files changed, 297 insertions(+), 297 deletions(-) rename {letsencrypt-apache => certbot-apache}/LICENSE.txt (100%) create mode 100644 certbot-apache/MANIFEST.in rename {letsencrypt-apache => certbot-apache}/README.rst (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/__init__.py (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/augeas_configurator.py (96%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/augeas_lens/README (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/augeas_lens/httpd.aug (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/centos-options-ssl-apache.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/configurator.py (95%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/constants.py (92%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/display_ops.py (94%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/obj.py (99%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/options-ssl-apache.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/parser.py (99%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/__init__.py (100%) create mode 100644 certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/apache-conf-test (92%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/failing/missing-double-quote-1724.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/failing/multivhost-1093.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/failing/multivhost-1093b.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/1626-1531.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/README.modules (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/anarcat-1531.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/drupal-htaccess-1531.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/example-1755.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/example-ssl.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/example.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/finalize-1243.conf (97%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/graphite-quote-1934.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/ipv6-1143.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/ipv6-1143b.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/ipv6-1143c.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/ipv6-1143d.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/missing-quote-1724.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/modmacro-1385.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/owncloud-1264.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/rewrite-quote-1960.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/roundcube-1222.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/section-continuations-2525.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/semacode-1598.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/augeas_configurator_test.py (96%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/complex_parsing_test.py (96%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/configurator_test.py (90%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/constants_test.py (75%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/display_ops_test.py (71%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/obj_test.py (92%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/parser_test.py (83%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/complex_parsing/apache2.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/complex_parsing/conf-enabled/dummy.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/complex_parsing/test_fnmatch.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/complex_parsing/test_variables.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf (88%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/default_vhost/sites (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load (100%) rename {letsencrypt-apache/docs/_static => certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled}/.gitignore (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf (100%) rename letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf => certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf (91%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf (88%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf (89%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf (100%) create mode 120000 certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf (100%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/testdata/debian_apache_2_4/multiple_vhosts/sites (56%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/tls_sni_01_test.py (91%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tests/util.py (87%) rename {letsencrypt-apache/letsencrypt_apache => certbot-apache/certbot_apache}/tls_sni_01.py (98%) rename {letsencrypt-apache => certbot-apache}/docs/.gitignore (100%) rename {letsencrypt-apache => certbot-apache}/docs/Makefile (96%) rename {letsencrypt-apache/docs/_templates => certbot-apache/docs/_static}/.gitignore (100%) rename {letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled => certbot-apache/docs/_templates}/.gitignore (100%) rename {letsencrypt-apache => certbot-apache}/docs/api.rst (100%) create mode 100644 certbot-apache/docs/api/augeas_configurator.rst create mode 100644 certbot-apache/docs/api/configurator.rst create mode 100644 certbot-apache/docs/api/display_ops.rst create mode 100644 certbot-apache/docs/api/obj.rst create mode 100644 certbot-apache/docs/api/parser.rst create mode 100644 certbot-apache/docs/api/tls_sni_01.rst rename {letsencrypt-apache => certbot-apache}/docs/conf.py (94%) rename {letsencrypt-apache => certbot-apache}/docs/index.rst (74%) rename {letsencrypt-apache => certbot-apache}/docs/make.bat (97%) rename {letsencrypt-apache => certbot-apache}/readthedocs.org.requirements.txt (94%) rename {letsencrypt-apache => certbot-apache}/setup.py (89%) delete mode 100644 letsencrypt-apache/MANIFEST.in delete mode 100644 letsencrypt-apache/docs/api/augeas_configurator.rst delete mode 100644 letsencrypt-apache/docs/api/configurator.rst delete mode 100644 letsencrypt-apache/docs/api/display_ops.rst delete mode 100644 letsencrypt-apache/docs/api/obj.rst delete mode 100644 letsencrypt-apache/docs/api/parser.rst delete mode 100644 letsencrypt-apache/docs/api/tls_sni_01.rst delete mode 100644 letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt delete mode 120000 letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf diff --git a/letsencrypt-apache/LICENSE.txt b/certbot-apache/LICENSE.txt similarity index 100% rename from letsencrypt-apache/LICENSE.txt rename to certbot-apache/LICENSE.txt diff --git a/certbot-apache/MANIFEST.in b/certbot-apache/MANIFEST.in new file mode 100644 index 000000000..3e594a953 --- /dev/null +++ b/certbot-apache/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +recursive-include certbot_apache/tests/testdata * +include certbot_apache/centos-options-ssl-apache.conf +include certbot_apache/options-ssl-apache.conf +recursive-include certbot_apache/augeas_lens *.aug diff --git a/letsencrypt-apache/README.rst b/certbot-apache/README.rst similarity index 100% rename from letsencrypt-apache/README.rst rename to certbot-apache/README.rst diff --git a/letsencrypt-apache/letsencrypt_apache/__init__.py b/certbot-apache/certbot_apache/__init__.py similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/__init__.py rename to certbot-apache/certbot_apache/__init__.py diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_configurator.py b/certbot-apache/certbot_apache/augeas_configurator.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/augeas_configurator.py rename to certbot-apache/certbot_apache/augeas_configurator.py index 9b51c32a9..12753541c 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_configurator.py +++ b/certbot-apache/certbot_apache/augeas_configurator.py @@ -3,11 +3,11 @@ import logging import augeas -from letsencrypt import errors -from letsencrypt import reverter -from letsencrypt.plugins import common +from certbot import errors +from certbot import reverter +from certbot.plugins import common -from letsencrypt_apache import constants +from certbot_apache import constants logger = logging.getLogger(__name__) @@ -16,14 +16,14 @@ class AugeasConfigurator(common.Plugin): """Base Augeas Configurator class. :ivar config: Configuration. - :type config: :class:`~letsencrypt.interfaces.IConfig` + :type config: :class:`~certbot.interfaces.IConfig` :ivar aug: Augeas object :type aug: :class:`augeas.Augeas` :ivar str save_notes: Human-readable configuration change notes :ivar reverter: saves and reverts checkpoints - :type reverter: :class:`letsencrypt.reverter.Reverter` + :type reverter: :class:`certbot.reverter.Reverter` """ def __init__(self, *args, **kwargs): diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/README b/certbot-apache/certbot_apache/augeas_lens/README similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/augeas_lens/README rename to certbot-apache/certbot_apache/augeas_lens/README diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/certbot-apache/certbot_apache/augeas_lens/httpd.aug similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug rename to certbot-apache/certbot_apache/augeas_lens/httpd.aug diff --git a/letsencrypt-apache/letsencrypt_apache/centos-options-ssl-apache.conf b/certbot-apache/certbot_apache/centos-options-ssl-apache.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/centos-options-ssl-apache.conf rename to certbot-apache/certbot_apache/centos-options-ssl-apache.conf diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py similarity index 95% rename from letsencrypt-apache/letsencrypt_apache/configurator.py rename to certbot-apache/certbot_apache/configurator.py index b2c843251..5681373b6 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -13,18 +13,18 @@ import zope.interface from acme import challenges -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util +from certbot import errors +from certbot import interfaces +from certbot import le_util -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_apache import augeas_configurator -from letsencrypt_apache import constants -from letsencrypt_apache import display_ops -from letsencrypt_apache import tls_sni_01 -from letsencrypt_apache import obj -from letsencrypt_apache import parser +from certbot_apache import augeas_configurator +from certbot_apache import constants +from certbot_apache import display_ops +from certbot_apache import tls_sni_01 +from certbot_apache import obj +from certbot_apache import parser from collections import defaultdict @@ -70,14 +70,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): 14.04 Apache 2.4 and it works for Ubuntu 12.04 Apache 2.2 :ivar config: Configuration. - :type config: :class:`~letsencrypt.interfaces.IConfig` + :type config: :class:`~certbot.interfaces.IConfig` :ivar parser: Handles low level parsing - :type parser: :class:`~letsencrypt_apache.parser` + :type parser: :class:`~certbot_apache.parser` :ivar tup version: version of Apache :ivar list vhosts: All vhosts found in the configuration - (:class:`list` of :class:`~letsencrypt_apache.obj.VirtualHost`) + (:class:`list` of :class:`~certbot_apache.obj.VirtualHost`) :ivar dict assoc: Mapping between domains and vhosts @@ -205,7 +205,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): installed, the VirtualHost is enabled if it isn't already. .. todo:: Might be nice to remove chain directive if none exists - This shouldn't happen within letsencrypt though + This shouldn't happen within certbot though :raises errors.PluginError: When unable to deploy certificate due to a lack of directives @@ -290,7 +290,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param bool temp: whether the vhost is only used temporarily :returns: ssl vhost associated with name - :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` + :rtype: :class:`~certbot_apache.obj.VirtualHost` :raises .errors.PluginError: If no vhost is available or chosen @@ -472,7 +472,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Helper function for get_virtual_hosts(). :param host: In progress vhost whose names will be added - :type host: :class:`~letsencrypt_apache.obj.VirtualHost` + :type host: :class:`~certbot_apache.obj.VirtualHost` """ # Take the final ServerName as each overrides the previous @@ -498,7 +498,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str path: Augeas path to virtual host :returns: newly created vhost - :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` + :rtype: :class:`~certbot_apache.obj.VirtualHost` """ addrs = set() @@ -534,7 +534,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): def get_virtual_hosts(self): """Returns list of virtual hosts found in the Apache configuration. - :returns: List of :class:`~letsencrypt_apache.obj.VirtualHost` + :returns: List of :class:`~certbot_apache.obj.VirtualHost` objects found in configuration :rtype: list @@ -572,7 +572,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): now NameVirtualHosts. If version is earlier than 2.4, check if addr has a NameVirtualHost directive in the Apache config - :param letsencrypt_apache.obj.Addr target_addr: vhost address + :param certbot_apache.obj.Addr target_addr: vhost address :returns: Success :rtype: bool @@ -590,7 +590,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Adds NameVirtualHost directive for given address. :param addr: Address that will be added as NameVirtualHost directive - :type addr: :class:`~letsencrypt_apache.obj.Addr` + :type addr: :class:`~certbot_apache.obj.Addr` """ loc = parser.get_aug_path(self.parser.loc["name"]) @@ -679,7 +679,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks to see if the server is ready for SNI challenges. :param addrs: Addresses to check SNI compatibility - :type addrs: :class:`~letsencrypt_apache.obj.Addr` + :type addrs: :class:`~certbot_apache.obj.Addr` """ # Version 2.4 and later are automatically SNI ready. @@ -697,15 +697,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Duplicates vhost and adds default ssl options New vhost will reside as (nonssl_vhost.path) + - ``letsencrypt_apache.constants.os_constant("le_vhost_ext")`` + ``certbot_apache.constants.os_constant("le_vhost_ext")`` .. note:: This function saves the configuration :param nonssl_vhost: Valid VH that doesn't have SSLEngine on - :type nonssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type nonssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :returns: SSL vhost - :rtype: :class:`~letsencrypt_apache.obj.VirtualHost` + :rtype: :class:`~certbot_apache.obj.VirtualHost` :raises .errors.PluginError: If more than one virtual host is in the file or if plugin is unable to write/read vhost files. @@ -911,7 +911,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): https://httpd.apache.org/docs/2.2/mod/core.html#namevirtualhost :param vhost: New virtual host that was recently created. - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` """ need_to_save = False @@ -951,9 +951,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param str domain: domain to enhance :param str enhancement: enhancement type defined in - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :param options: options for the enhancement - See :const:`~letsencrypt.constants.ENHANCEMENTS` + See :const:`~certbot.constants.ENHANCEMENTS` documentation for appropriate parameter. :raises .errors.PluginError: If Enhancement is not supported, or if @@ -981,14 +981,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. note:: This function saves the configuration :param ssl_vhost: Destination of traffic, an ssl enabled vhost - :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :param header_substring: string that uniquely identifies a header. e.g: Strict-Transport-Security, Upgrade-Insecure-Requests. :type str :returns: Success, general_vhost (HTTP vhost) - :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) + :rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`) :raises .errors.PluginError: If no viable HTTP host can be created or set with header header_substring. @@ -1016,7 +1016,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): contains the string header_substring. :param ssl_vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :param header_substring: string that uniquely identifies a header. e.g: Strict-Transport-Security, Upgrade-Insecure-Requests. @@ -1053,13 +1053,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. note:: This function saves the configuration :param ssl_vhost: Destination of traffic, an ssl enabled vhost - :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available :returns: Success, general_vhost (HTTP vhost) - :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) + :rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`) :raises .errors.PluginError: If no viable HTTP host can be created or used for the redirect. @@ -1084,10 +1084,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self._create_redirect_vhost(ssl_vhost) else: # Check if LetsEncrypt redirection already exists - self._verify_no_letsencrypt_redirect(general_vh) + self._verify_no_certbot_redirect(general_vh) # Note: if code flow gets here it means we didn't find the exact - # letsencrypt RewriteRule config for redirection. Finding + # certbot RewriteRule config for redirection. Finding # another RewriteRule is likely to be fine in most or all cases, # but redirect loops are possible in very obscure cases; see #1620 # for reasoning. @@ -1121,17 +1121,17 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): logger.info("Redirecting vhost in %s to ssl vhost in %s", general_vh.filep, ssl_vhost.filep) - def _verify_no_letsencrypt_redirect(self, vhost): - """Checks to see if a redirect was already installed by letsencrypt. + def _verify_no_certbot_redirect(self, vhost): + """Checks to see if a redirect was already installed by certbot. Checks to see if virtualhost already contains a rewrite rule that is identical to Letsencrypt's redirection rewrite rule. :param vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :raises errors.PluginEnhancementAlreadyPresent: When the exact - letsencrypt redirection WriteRule exists in virtual host. + certbot redirection WriteRule exists in virtual host. """ rewrite_path = self.parser.find_dir( "RewriteRule", None, start=vhost.path) @@ -1160,7 +1160,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks if there exists a RewriteRule directive in vhost :param vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :returns: True if a RewriteRule directive exists. :rtype: bool @@ -1174,7 +1174,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks if a RewriteEngine directive is on :param vhost: vhost to check - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` """ rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on", @@ -1187,10 +1187,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Creates an http_vhost specifically to redirect for the ssl_vhost. :param ssl_vhost: ssl vhost - :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type ssl_vhost: :class:`~certbot_apache.obj.VirtualHost` :returns: tuple of the form - (`success`, :class:`~letsencrypt_apache.obj.VirtualHost`) + (`success`, :class:`~certbot_apache.obj.VirtualHost`) :rtype: tuple """ @@ -1369,7 +1369,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): .. todo:: Make sure link is not broken... :param vhost: vhost to enable - :type vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + :type vhost: :class:`~certbot_apache.obj.VirtualHost` :raises .errors.NotSupportedError: If filesystem layout is not supported. @@ -1452,7 +1452,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not le_util.exe_exists(self.conf("dismod")): raise errors.MisconfigurationError( "Unable to find a2dismod, please make sure a2enmod and " - "a2dismod are configured correctly for letsencrypt.") + "a2dismod are configured correctly for certbot.") self.reverter.register_undo_command( temp, [self.conf("dismod"), mod_name]) @@ -1639,7 +1639,7 @@ def install_ssl_options_conf(options_ssl): required. """ # XXX if we ever try to enforce a local privilege boundary (eg, running - # letsencrypt for unprivileged users via setuid), this function will need + # certbot for unprivileged users via setuid), this function will need # to be modified. # XXX if the user is in security-autoupdate mode, we should be willing to diff --git a/letsencrypt-apache/letsencrypt_apache/constants.py b/certbot-apache/certbot_apache/constants.py similarity index 92% rename from letsencrypt-apache/letsencrypt_apache/constants.py rename to certbot-apache/certbot_apache/constants.py index 8b502b4d8..f3226572c 100644 --- a/letsencrypt-apache/letsencrypt_apache/constants.py +++ b/certbot-apache/certbot_apache/constants.py @@ -1,6 +1,6 @@ """Apache plugin constants.""" import pkg_resources -from letsencrypt import le_util +from certbot import le_util CLI_DEFAULTS_DEBIAN = dict( @@ -18,7 +18,7 @@ CLI_DEFAULTS_DEBIAN = dict( handle_sites=True, challenge_location="/etc/apache2", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "certbot_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS_CENTOS = dict( server_root="/etc/httpd", @@ -35,7 +35,7 @@ CLI_DEFAULTS_CENTOS = dict( handle_sites=False, challenge_location="/etc/httpd/conf.d", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "centos-options-ssl-apache.conf") + "certbot_apache", "centos-options-ssl-apache.conf") ) CLI_DEFAULTS_GENTOO = dict( server_root="/etc/apache2", @@ -52,7 +52,7 @@ CLI_DEFAULTS_GENTOO = dict( handle_sites=False, challenge_location="/etc/apache2/vhosts.d", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "certbot_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS_DARWIN = dict( server_root="/etc/apache2", @@ -69,7 +69,7 @@ CLI_DEFAULTS_DARWIN = dict( handle_sites=False, challenge_location="/etc/apache2/other", MOD_SSL_CONF_SRC=pkg_resources.resource_filename( - "letsencrypt_apache", "options-ssl-apache.conf") + "certbot_apache", "options-ssl-apache.conf") ) CLI_DEFAULTS = { "debian": CLI_DEFAULTS_DEBIAN, @@ -87,7 +87,7 @@ MOD_SSL_CONF_DEST = "options-ssl-apache.conf" """Name of the mod_ssl config file as saved in `IConfig.config_dir`.""" AUGEAS_LENS_DIR = pkg_resources.resource_filename( - "letsencrypt_apache", "augeas_lens") + "certbot_apache", "augeas_lens") """Path to the Augeas lens directory""" REWRITE_HTTPS_ARGS = [ diff --git a/letsencrypt-apache/letsencrypt_apache/display_ops.py b/certbot-apache/certbot_apache/display_ops.py similarity index 94% rename from letsencrypt-apache/letsencrypt_apache/display_ops.py rename to certbot-apache/certbot_apache/display_ops.py index 4c01579cc..c9359e7d3 100644 --- a/letsencrypt-apache/letsencrypt_apache/display_ops.py +++ b/certbot-apache/certbot_apache/display_ops.py @@ -4,10 +4,10 @@ import os import zope.component -from letsencrypt import errors -from letsencrypt import interfaces +from certbot import errors +from certbot import interfaces -import letsencrypt.display.util as display_util +import certbot.display.util as display_util logger = logging.getLogger(__name__) @@ -50,7 +50,7 @@ def _vhost_menu(domain, vhosts): if free_chars < 2: logger.debug("Display size is too small for " - "letsencrypt_apache.display_ops._vhost_menu()") + "certbot_apache.display_ops._vhost_menu()") # This runs the edge off the screen, but it doesn't cause an "error" filename_size = 1 disp_name_size = 1 diff --git a/letsencrypt-apache/letsencrypt_apache/obj.py b/certbot-apache/certbot_apache/obj.py similarity index 99% rename from letsencrypt-apache/letsencrypt_apache/obj.py rename to certbot-apache/certbot_apache/obj.py index c05ee4bcc..b88b22428 100644 --- a/letsencrypt-apache/letsencrypt_apache/obj.py +++ b/certbot-apache/certbot_apache/obj.py @@ -1,7 +1,7 @@ """Module contains classes used by the Apache Configurator.""" import re -from letsencrypt.plugins import common +from certbot.plugins import common class Addr(common.Addr): diff --git a/letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf b/certbot-apache/certbot_apache/options-ssl-apache.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/options-ssl-apache.conf rename to certbot-apache/certbot_apache/options-ssl-apache.conf diff --git a/letsencrypt-apache/letsencrypt_apache/parser.py b/certbot-apache/certbot_apache/parser.py similarity index 99% rename from letsencrypt-apache/letsencrypt_apache/parser.py rename to certbot-apache/certbot_apache/parser.py index f49ac0acc..321546eb3 100644 --- a/letsencrypt-apache/letsencrypt_apache/parser.py +++ b/certbot-apache/certbot_apache/parser.py @@ -6,9 +6,9 @@ import os import re import subprocess -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache import constants +from certbot_apache import constants logger = logging.getLogger(__name__) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/__init__.py b/certbot-apache/certbot_apache/tests/__init__.py similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/__init__.py rename to certbot-apache/certbot_apache/tests/__init__.py diff --git a/certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt b/certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt new file mode 100644 index 000000000..c3606fefe --- /dev/null +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/NEEDED.txt @@ -0,0 +1,6 @@ +Issues for which some kind of test case should be constructable, but we do not +currently have one: + +https://github.com/certbot/certbot/issues/1213 +https://github.com/certbot/certbot/issues/1602 + diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test b/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test similarity index 92% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test rename to certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test index 5725508e9..b2a453fb9 100755 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test @@ -59,7 +59,7 @@ trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... Setup - RESULT=`echo c | sudo env "PATH=$PATH" letsencrypt -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo env "PATH=$PATH" certbot -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then echo passed else diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/failing/missing-double-quote-1724.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093b.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093b.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/failing/multivhost-1093b.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/failing/multivhost-1093b.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/1626-1531.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/1626-1531.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/1626-1531.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/1626-1531.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/README.modules b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/README.modules similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/README.modules rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/README.modules diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/anarcat-1531.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/anarcat-1531.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/anarcat-1531.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/anarcat-1531.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-errordocument-arg-1724.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/drupal-htaccess-1531.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-1755.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-1755.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-1755.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-1755.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example-ssl.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/example-ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/example.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.apache2.conf.txt diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf similarity index 97% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf index 0918e5669..0b34ee5f0 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/finalize-1243.conf +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf @@ -41,7 +41,7 @@ Listen 443 SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key SSLCertificateChainFile /etc/ssl/certs/ssl-cert-snakeoil.pem -Include /etc/letsencrypt/options-ssl-apache.conf +Include /etc/certbot/options-ssl-apache.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/graphite-quote-1934.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143b.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143b.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143b.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143c.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143c.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143c.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143d.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/ipv6-1143d.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/ipv6-1143d.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/missing-quote-1724.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/missing-quote-1724.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/missing-quote-1724.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/missing-quote-1724.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/modmacro-1385.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/modmacro-1385.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/modmacro-1385.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/modmacro-1385.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/owncloud-1264.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/owncloud-1264.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/owncloud-1264.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/owncloud-1264.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/rewrite-quote-1960.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/roundcube-1222.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/roundcube-1222.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/roundcube-1222.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/roundcube-1222.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/section-continuations-2525.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-continuations-2525.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/section-continuations-2525.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-continuations-2525.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/semacode-1598.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/semacode-1598.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/semacode-1598.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/semacode-1598.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/sslrequire-wordlist-1827.htaccess diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf rename to certbot-apache/certbot_apache/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py b/certbot-apache/certbot_apache/tests/augeas_configurator_test.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py rename to certbot-apache/certbot_apache/tests/augeas_configurator_test.py index bf95f72ce..c55f27ff0 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/augeas_configurator_test.py +++ b/certbot-apache/certbot_apache/tests/augeas_configurator_test.py @@ -1,13 +1,13 @@ -"""Test for letsencrypt_apache.augeas_configurator.""" +"""Test for certbot_apache.augeas_configurator.""" import os import shutil import unittest import mock -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class AugeasConfiguratorTest(util.ApacheTest): diff --git a/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py b/certbot-apache/certbot_apache/tests/complex_parsing_test.py similarity index 96% rename from letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py rename to certbot-apache/certbot_apache/tests/complex_parsing_test.py index 1fc5281c1..079d7e95f 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/complex_parsing_test.py +++ b/certbot-apache/certbot_apache/tests/complex_parsing_test.py @@ -1,11 +1,11 @@ -"""Tests for letsencrypt_apache.parser.""" +"""Tests for certbot_apache.parser.""" import os import shutil import unittest -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class ComplexParserTest(util.ParserTest): @@ -88,7 +88,7 @@ class ComplexParserTest(util.ParserTest): def verify_fnmatch(self, arg, hit=True): """Test if Include was correctly parsed.""" - from letsencrypt_apache import parser + from certbot_apache import parser self.parser.add_dir(parser.get_aug_path(self.parser.loc["default"]), "Include", [arg]) if hit: diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py similarity index 90% rename from letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py rename to certbot-apache/certbot_apache/tests/configurator_test.py index 927d918ae..f2f78c8f9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/certbot-apache/certbot_apache/tests/configurator_test.py @@ -1,5 +1,5 @@ # pylint: disable=too-many-public-methods -"""Test for letsencrypt_apache.configurator.""" +"""Test for certbot_apache.configurator.""" import os import shutil import socket @@ -9,15 +9,15 @@ import mock from acme import challenges -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt.tests import acme_util +from certbot.tests import acme_util -from letsencrypt_apache import configurator -from letsencrypt_apache import obj +from certbot_apache import configurator +from certbot_apache import obj -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class MultipleVhostsTest(util.ApacheTest): @@ -38,7 +38,7 @@ class MultipleVhostsTest(util.ApacheTest): def mocked_deploy_cert(*args, **kwargs): """a helper to mock a deployed cert""" - with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"): + with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"): config.real_deploy_cert(*args, **kwargs) self.config.deploy_cert = mocked_deploy_cert return self.config @@ -48,14 +48,14 @@ class MultipleVhostsTest(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + @mock.patch("certbot_apache.configurator.le_util.exe_exists") def test_prepare_no_install(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( errors.NoInstallationError, self.config.prepare) - @mock.patch("letsencrypt_apache.parser.ApacheParser") - @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + @mock.patch("certbot_apache.parser.ApacheParser") + @mock.patch("certbot_apache.configurator.le_util.exe_exists") def test_prepare_version(self, mock_exe_exists, _): mock_exe_exists.return_value = True self.config.version = None @@ -65,8 +65,8 @@ class MultipleVhostsTest(util.ApacheTest): self.assertRaises( errors.NotSupportedError, self.config.prepare) - @mock.patch("letsencrypt_apache.parser.ApacheParser") - @mock.patch("letsencrypt_apache.configurator.le_util.exe_exists") + @mock.patch("certbot_apache.parser.ApacheParser") + @mock.patch("certbot_apache.configurator.le_util.exe_exists") def test_prepare_old_aug(self, mock_exe_exists, _): mock_exe_exists.return_value = True self.config.config_test = mock.Mock() @@ -76,7 +76,7 @@ class MultipleVhostsTest(util.ApacheTest): errors.NotSupportedError, self.config.prepare) def test_add_parser_arguments(self): # pylint: disable=no-self-use - from letsencrypt_apache.configurator import ApacheConfigurator + from certbot_apache.configurator import ApacheConfigurator # Weak test.. ApacheConfigurator.add_parser_arguments(mock.MagicMock()) @@ -85,10 +85,10 @@ class MultipleVhostsTest(util.ApacheTest): mock_getutility.notification = mock.MagicMock(return_value=True) names = self.config.get_all_names() self.assertEqual(names, set( - ["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"])) + ["certbot.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"])) @mock.patch("zope.component.getUtility") - @mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr") + @mock.patch("certbot_apache.configurator.socket.gethostbyaddr") def test_get_all_names_addrs(self, mock_gethost, mock_getutility): mock_gethost.side_effect = [("google.com", "", ""), socket.error] notification = mock.Mock() @@ -106,7 +106,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertEqual(len(names), 6) self.assertTrue("zombo.com" in names) self.assertTrue("google.com" in names) - self.assertTrue("letsencrypt.demo" in names) + self.assertTrue("certbot.demo" in names) def test_add_servernames_alias(self): self.config.parser.add_dir( @@ -139,25 +139,25 @@ class MultipleVhostsTest(util.ApacheTest): # Handle case of non-debian layout get_virtual_hosts with mock.patch( - "letsencrypt_apache.configurator.ApacheConfigurator.conf" + "certbot_apache.configurator.ApacheConfigurator.conf" ) as mock_conf: mock_conf.return_value = False vhs = self.config.get_virtual_hosts() self.assertEqual(len(vhs), 7) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_none_avail(self, mock_select): mock_select.return_value = None self.assertRaises( errors.PluginError, self.config.choose_vhost, "none.com") - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_ssl(self, mock_select): mock_select.return_value = self.vh_truth[1] self.assertEqual( self.vh_truth[1], self.config.choose_vhost("none.com")) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_non_ssl(self, mock_select): mock_select.return_value = self.vh_truth[0] chosen_vhost = self.config.choose_vhost("none.com") @@ -169,13 +169,13 @@ class MultipleVhostsTest(util.ApacheTest): self.assertFalse(self.vh_truth[0].ssl) self.assertTrue(chosen_vhost.ssl) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_with_temp(self, mock_select): mock_select.return_value = self.vh_truth[0] chosen_vhost = self.config.choose_vhost("none.com", temp=True) self.assertEqual(self.vh_truth[0], chosen_vhost) - @mock.patch("letsencrypt_apache.display_ops.select_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_select_vhost_conflicting_non_ssl(self, mock_select): mock_select.return_value = self.vh_truth[3] conflicting_vhost = obj.VirtualHost( @@ -203,7 +203,7 @@ class MultipleVhostsTest(util.ApacheTest): def test_find_best_vhost(self): # pylint: disable=protected-access self.assertEqual( - self.vh_truth[3], self.config._find_best_vhost("letsencrypt.demo")) + self.vh_truth[3], self.config._find_best_vhost("certbot.demo")) self.assertEqual( self.vh_truth[0], self.config._find_best_vhost("encryption-example.demo")) @@ -224,7 +224,7 @@ class MultipleVhostsTest(util.ApacheTest): # Assume only the two default vhosts. self.config.vhosts = [ vh for vh in self.config.vhosts - if vh.name not in ["letsencrypt.demo", "encryption-example.demo"] + if vh.name not in ["certbot.demo", "encryption-example.demo"] and "*.blue.purple.com" not in vh.aliases ] @@ -254,9 +254,9 @@ class MultipleVhostsTest(util.ApacheTest): self.config.is_site_enabled, "irrelevant") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") - @mock.patch("letsencrypt_apache.parser.subprocess.Popen") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") + @mock.patch("certbot_apache.parser.subprocess.Popen") def test_enable_mod(self, mock_popen, mock_exe_exists, mock_run_script): mock_popen().communicate.return_value = ("Define: DUMP_RUN_CFG", "") mock_popen().returncode = 0 @@ -273,7 +273,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertRaises( errors.NotSupportedError, self.config.enable_mod, "ssl") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.exe_exists") def test_enable_mod_no_disable(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( @@ -636,8 +636,8 @@ class MultipleVhostsTest(util.ApacheTest): self.config._add_name_vhost_if_necessary(self.vh_truth[0]) self.assertEqual(self.config.save.call_count, 2) - @mock.patch("letsencrypt_apache.configurator.tls_sni_01.ApacheTlsSni01.perform") - @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + @mock.patch("certbot_apache.configurator.tls_sni_01.ApacheTlsSni01.perform") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") def test_perform(self, mock_restart, mock_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded @@ -656,7 +656,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertEqual(mock_restart.call_count, 1) - @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") def test_cleanup(self, mock_restart): _, achall1, achall2 = self.get_achalls() @@ -669,7 +669,7 @@ class MultipleVhostsTest(util.ApacheTest): self.config.cleanup([achall2]) self.assertTrue(mock_restart.called) - @mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.restart") + @mock.patch("certbot_apache.configurator.ApacheConfigurator.restart") def test_cleanup_no_errors(self, mock_restart): _, achall1, achall2 = self.get_achalls() @@ -681,7 +681,7 @@ class MultipleVhostsTest(util.ApacheTest): self.config.cleanup([achall1, achall2]) self.assertTrue(mock_restart.called) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_get_version(self, mock_script): mock_script.return_value = ( "Server Version: Apache/2.4.2 (Debian)", "") @@ -703,21 +703,21 @@ class MultipleVhostsTest(util.ApacheTest): mock_script.side_effect = errors.SubprocessError("Can't find program") self.assertRaises(errors.PluginError, self.config.get_version) - @mock.patch("letsencrypt_apache.configurator.le_util.run_script") + @mock.patch("certbot_apache.configurator.le_util.run_script") def test_restart(self, _): self.config.restart() - @mock.patch("letsencrypt_apache.configurator.le_util.run_script") + @mock.patch("certbot_apache.configurator.le_util.run_script") def test_restart_bad_process(self, mock_run_script): mock_run_script.side_effect = [None, errors.SubprocessError] self.assertRaises(errors.MisconfigurationError, self.config.restart) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test(self, _): self.config.config_test() - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test_bad_process(self, mock_run_script): mock_run_script.side_effect = errors.SubprocessError @@ -747,7 +747,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertTrue(isinstance(self.config.get_chall_pref(""), list)) def test_install_ssl_options_conf(self): - from letsencrypt_apache.configurator import install_ssl_options_conf + from certbot_apache.configurator import install_ssl_options_conf path = os.path.join(self.work_dir, "test_it") install_ssl_options_conf(path) self.assertTrue(os.path.isfile(path)) @@ -756,7 +756,7 @@ class MultipleVhostsTest(util.ApacheTest): def test_supported_enhancements(self): self.assertTrue(isinstance(self.config.supported_enhancements(), list)) - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.exe_exists") def test_enhance_unknown_vhost(self, mock_exe): self.config.parser.modules.add("rewrite_module") mock_exe.return_value = True @@ -772,23 +772,23 @@ class MultipleVhostsTest(util.ApacheTest): def test_enhance_unknown_enhancement(self): self.assertRaises( errors.PluginError, - self.config.enhance, "letsencrypt.demo", "unknown_enhancement") + self.config.enhance, "certbot.demo", "unknown_enhancement") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_http_header_hsts(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") mock_exe.return_value = True - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "ensure-http-header", + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "ensure-http-header", "Strict-Transport-Security") self.assertTrue("headers_module" in self.config.parser.modules) - # Get the ssl vhost for letsencrypt.demo - ssl_vhost = self.config.assoc["letsencrypt.demo"] + # Get the ssl vhost for certbot.demo + ssl_vhost = self.config.assoc["certbot.demo"] # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -803,7 +803,7 @@ class MultipleVhostsTest(util.ApacheTest): # skip the enable mod self.config.parser.modules.add("headers_module") - # This will create an ssl vhost for letsencrypt.demo + # This will create an ssl vhost for certbot.demo self.config.enhance("encryption-example.demo", "ensure-http-header", "Strict-Transport-Security") @@ -812,21 +812,21 @@ class MultipleVhostsTest(util.ApacheTest): self.config.enhance, "encryption-example.demo", "ensure-http-header", "Strict-Transport-Security") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_http_header_uir(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() self.config.parser.modules.add("mod_ssl.c") mock_exe.return_value = True - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "ensure-http-header", + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "ensure-http-header", "Upgrade-Insecure-Requests") self.assertTrue("headers_module" in self.config.parser.modules) - # Get the ssl vhost for letsencrypt.demo - ssl_vhost = self.config.assoc["letsencrypt.demo"] + # Get the ssl vhost for certbot.demo + ssl_vhost = self.config.assoc["certbot.demo"] # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -841,7 +841,7 @@ class MultipleVhostsTest(util.ApacheTest): # skip the enable mod self.config.parser.modules.add("headers_module") - # This will create an ssl vhost for letsencrypt.demo + # This will create an ssl vhost for certbot.demo self.config.enhance("encryption-example.demo", "ensure-http-header", "Upgrade-Insecure-Requests") @@ -850,15 +850,15 @@ class MultipleVhostsTest(util.ApacheTest): self.config.enhance, "encryption-example.demo", "ensure-http-header", "Upgrade-Insecure-Requests") - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_redirect_well_formed_http(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True self.config.get_version = mock.Mock(return_value=(2, 2)) - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "redirect") + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "redirect") # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -894,8 +894,8 @@ class MultipleVhostsTest(util.ApacheTest): # pylint: disable=protected-access self.assertTrue(self.config._is_rewrite_engine_on(self.vh_truth[3])) - @mock.patch("letsencrypt.le_util.run_script") - @mock.patch("letsencrypt.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") def test_redirect_with_existing_rewrite(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True @@ -907,8 +907,8 @@ class MultipleVhostsTest(util.ApacheTest): "UnknownTarget"]) self.config.save() - # This will create an ssl vhost for letsencrypt.demo - self.config.enhance("letsencrypt.demo", "redirect") + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "redirect") # These are not immediately available in find_dir even with save() and # load(). They must be found in sites-available @@ -981,7 +981,7 @@ class MultipleVhostsTest(util.ApacheTest): normal_target = "RewriteRule ^/(.*) http://www.a.com:1234/$1 [L,R]" self.assertFalse(self.config._sift_line(normal_target)) - @mock.patch("letsencrypt_apache.configurator.zope.component.getUtility") + @mock.patch("certbot_apache.configurator.zope.component.getUtility") def test_make_vhost_ssl_with_existing_rewrite_rule(self, mock_get_utility): self.config.parser.modules.add("rewrite_module") @@ -1024,7 +1024,7 @@ class MultipleVhostsTest(util.ApacheTest): challenges.TLSSNI01( token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"), "pending"), - domain="letsencrypt.demo", account_key=account_key) + domain="certbot.demo", account_key=account_key) return account_key, achall1, achall2 diff --git a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py b/certbot-apache/certbot_apache/tests/constants_test.py similarity index 75% rename from letsencrypt-apache/letsencrypt_apache/tests/constants_test.py rename to certbot-apache/certbot_apache/tests/constants_test.py index 289b61bb1..d970c96be 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/constants_test.py +++ b/certbot-apache/certbot_apache/tests/constants_test.py @@ -1,26 +1,26 @@ -"""Test for letsencrypt_apache.configurator.""" +"""Test for certbot_apache.configurator.""" import mock import unittest -from letsencrypt_apache import constants +from certbot_apache import constants class ConstantsTest(unittest.TestCase): - @mock.patch("letsencrypt.le_util.get_os_info") + @mock.patch("certbot.le_util.get_os_info") def test_get_debian_value(self, os_info): os_info.return_value = ('Debian', '', '') self.assertEqual(constants.os_constant("vhost_root"), "/etc/apache2/sites-available") - @mock.patch("letsencrypt.le_util.get_os_info") + @mock.patch("certbot.le_util.get_os_info") def test_get_centos_value(self, os_info): os_info.return_value = ('CentOS Linux', '', '') self.assertEqual(constants.os_constant("vhost_root"), "/etc/httpd/conf.d") - @mock.patch("letsencrypt.le_util.get_os_info") + @mock.patch("certbot.le_util.get_os_info") def test_get_default_value(self, os_info): os_info.return_value = ('Nonexistent Linux', '', '') self.assertEqual(constants.os_constant("vhost_root"), diff --git a/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py b/certbot-apache/certbot_apache/tests/display_ops_test.py similarity index 71% rename from letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py rename to certbot-apache/certbot_apache/tests/display_ops_test.py index 124ba2f17..fd1e52fde 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/display_ops_test.py +++ b/certbot-apache/certbot_apache/tests/display_ops_test.py @@ -1,20 +1,20 @@ -"""Test letsencrypt_apache.display_ops.""" +"""Test certbot_apache.display_ops.""" import sys import unittest import mock import zope.component -from letsencrypt.display import util as display_util -from letsencrypt import errors +from certbot.display import util as display_util +from certbot import errors -from letsencrypt_apache import obj +from certbot_apache import obj -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class SelectVhostTest(unittest.TestCase): - """Tests for letsencrypt_apache.display_ops.select_vhost.""" + """Tests for certbot_apache.display_ops.select_vhost.""" def setUp(self): zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) @@ -24,15 +24,15 @@ class SelectVhostTest(unittest.TestCase): @classmethod def _call(cls, vhosts): - from letsencrypt_apache.display_ops import select_vhost + from certbot_apache.display_ops import select_vhost return select_vhost("example.com", vhosts) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_successful_choice(self, mock_util): mock_util().menu.return_value = (display_util.OK, 3) self.assertEqual(self.vhosts[3], self._call(self.vhosts)) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_noninteractive(self, mock_util): mock_util().menu.side_effect = errors.MissingCommandlineFlag("no vhost default") try: @@ -40,7 +40,7 @@ class SelectVhostTest(unittest.TestCase): except errors.MissingCommandlineFlag as e: self.assertTrue("VirtualHost directives" in e.message) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_more_info_cancel(self, mock_util): mock_util().menu.side_effect = [ (display_util.HELP, 1), @@ -54,9 +54,9 @@ class SelectVhostTest(unittest.TestCase): def test_no_vhosts(self): self.assertEqual(self._call([]), None) - @mock.patch("letsencrypt_apache.display_ops.display_util") - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") - @mock.patch("letsencrypt_apache.display_ops.logger") + @mock.patch("certbot_apache.display_ops.display_util") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.logger") def test_small_display(self, mock_logger, mock_util, mock_display_util): mock_display_util.WIDTH = 20 mock_util().menu.return_value = (display_util.OK, 0) @@ -64,7 +64,7 @@ class SelectVhostTest(unittest.TestCase): self.assertEqual(mock_logger.debug.call_count, 1) - @mock.patch("letsencrypt_apache.display_ops.zope.component.getUtility") + @mock.patch("certbot_apache.display_ops.zope.component.getUtility") def test_multiple_names(self, mock_util): mock_util().menu.return_value = (display_util.OK, 5) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/obj_test.py b/certbot-apache/certbot_apache/tests/obj_test.py similarity index 92% rename from letsencrypt-apache/letsencrypt_apache/tests/obj_test.py rename to certbot-apache/certbot_apache/tests/obj_test.py index a469702f1..4c3d331be 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/obj_test.py +++ b/certbot-apache/certbot_apache/tests/obj_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_apache.obj.""" +"""Tests for certbot_apache.obj.""" import unittest @@ -6,8 +6,8 @@ class VirtualHostTest(unittest.TestCase): """Test the VirtualHost class.""" def setUp(self): - from letsencrypt_apache.obj import Addr - from letsencrypt_apache.obj import VirtualHost + from certbot_apache.obj import Addr + from certbot_apache.obj import VirtualHost self.addr1 = Addr.fromstring("127.0.0.1") self.addr2 = Addr.fromstring("127.0.0.1:443") @@ -33,8 +33,8 @@ class VirtualHostTest(unittest.TestCase): self.assertFalse(self.vhost1 != self.vhost1b) def test_conflicts(self): - from letsencrypt_apache.obj import Addr - from letsencrypt_apache.obj import VirtualHost + from certbot_apache.obj import Addr + from certbot_apache.obj import VirtualHost complex_vh = VirtualHost( "fp", "vhp", @@ -51,7 +51,7 @@ class VirtualHostTest(unittest.TestCase): self.addr_default])) def test_same_server(self): - from letsencrypt_apache.obj import VirtualHost + from certbot_apache.obj import VirtualHost no_name1 = VirtualHost( "fp", "vhp", set([self.addr1]), False, False, None) no_name2 = VirtualHost( @@ -74,7 +74,7 @@ class VirtualHostTest(unittest.TestCase): class AddrTest(unittest.TestCase): """Test obj.Addr.""" def setUp(self): - from letsencrypt_apache.obj import Addr + from certbot_apache.obj import Addr self.addr = Addr.fromstring("*:443") self.addr1 = Addr.fromstring("127.0.0.1") @@ -89,7 +89,7 @@ class AddrTest(unittest.TestCase): self.assertTrue(self.addr2.is_wildcard()) def test_get_sni_addr(self): - from letsencrypt_apache.obj import Addr + from certbot_apache.obj import Addr self.assertEqual( self.addr.get_sni_addr("443"), Addr.fromstring("*:443")) self.assertEqual( diff --git a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py b/certbot-apache/certbot_apache/tests/parser_test.py similarity index 83% rename from letsencrypt-apache/letsencrypt_apache/tests/parser_test.py rename to certbot-apache/certbot_apache/tests/parser_test.py index f4d4660c9..759ae1265 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/parser_test.py +++ b/certbot-apache/certbot_apache/tests/parser_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_apache.parser.""" +"""Tests for certbot_apache.parser.""" import os import shutil import unittest @@ -6,9 +6,9 @@ import unittest import augeas import mock -from letsencrypt import errors +from certbot import errors -from letsencrypt_apache.tests import util +from certbot_apache.tests import util class BasicParserTest(util.ParserTest): @@ -31,12 +31,12 @@ class BasicParserTest(util.ParserTest): def test_parse_file(self): """Test parse_file. - letsencrypt.conf is chosen as the test file as it will not be + certbot.conf is chosen as the test file as it will not be included during the normal course of execution. """ file_path = os.path.join( - self.config_path, "not-parsed-by-default", "letsencrypt.conf") + self.config_path, "not-parsed-by-default", "certbot.conf") self.parser._parse_file(file_path) # pylint: disable=protected-access @@ -72,7 +72,7 @@ class BasicParserTest(util.ParserTest): Path must be valid before attempting to add to augeas """ - from letsencrypt_apache.parser import get_aug_path + from certbot_apache.parser import get_aug_path # This makes sure that find_dir will work self.parser.modules.add("mod_ssl.c") @@ -86,7 +86,7 @@ class BasicParserTest(util.ParserTest): self.assertTrue("IfModule" in matches[0]) def test_add_dir_to_ifmodssl_multiple(self): - from letsencrypt_apache.parser import get_aug_path + from certbot_apache.parser import get_aug_path # This makes sure that find_dir will work self.parser.modules.add("mod_ssl.c") @@ -100,11 +100,11 @@ class BasicParserTest(util.ParserTest): self.assertTrue("IfModule" in matches[0]) def test_get_aug_path(self): - from letsencrypt_apache.parser import get_aug_path + from certbot_apache.parser import get_aug_path self.assertEqual("/files/etc/apache", get_aug_path("/etc/apache")) def test_set_locations(self): - with mock.patch("letsencrypt_apache.parser.os.path") as mock_path: + with mock.patch("certbot_apache.parser.os.path") as mock_path: mock_path.isfile.side_effect = [False, False] @@ -114,7 +114,7 @@ class BasicParserTest(util.ParserTest): self.assertEqual(results["default"], results["listen"]) self.assertEqual(results["default"], results["name"]) - @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_variables(self, mock_cfg): mock_cfg.return_value = ( 'ServerRoot: "/etc/apache2"\n' @@ -139,7 +139,7 @@ class BasicParserTest(util.ParserTest): self.parser.update_runtime_variables() self.assertEqual(self.parser.variables, expected_vars) - @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_update_runtime_vars_bad_output(self, mock_cfg): mock_cfg.return_value = "Define: TLS=443=24" self.parser.update_runtime_variables() @@ -148,8 +148,8 @@ class BasicParserTest(util.ParserTest): self.assertRaises( errors.PluginError, self.parser.update_runtime_variables) - @mock.patch("letsencrypt_apache.constants.os_constant") - @mock.patch("letsencrypt_apache.parser.subprocess.Popen") + @mock.patch("certbot_apache.constants.os_constant") + @mock.patch("certbot_apache.parser.subprocess.Popen") def test_update_runtime_vars_bad_ctl(self, mock_popen, mock_const): mock_popen.side_effect = OSError mock_const.return_value = "nonexistent" @@ -157,7 +157,7 @@ class BasicParserTest(util.ParserTest): errors.MisconfigurationError, self.parser.update_runtime_variables) - @mock.patch("letsencrypt_apache.parser.subprocess.Popen") + @mock.patch("certbot_apache.parser.subprocess.Popen") def test_update_runtime_vars_bad_exit(self, mock_popen): mock_popen().communicate.return_value = ("", "") mock_popen.returncode = -1 @@ -177,9 +177,9 @@ class ParserInitTest(util.ApacheTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_apache.parser.ApacheParser._get_runtime_cfg") + @mock.patch("certbot_apache.parser.ApacheParser._get_runtime_cfg") def test_unparsable(self, mock_cfg): - from letsencrypt_apache.parser import ApacheParser + from certbot_apache.parser import ApacheParser mock_cfg.return_value = ('Define: TEST') self.assertRaises( errors.PluginError, @@ -187,9 +187,9 @@ class ParserInitTest(util.ApacheTest): "/dummy/vhostpath", version=(2, 2, 22)) def test_root_normalized(self): - from letsencrypt_apache.parser import ApacheParser + from certbot_apache.parser import ApacheParser - with mock.patch("letsencrypt_apache.parser.ApacheParser." + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): path = os.path.join( self.temp_dir, @@ -201,8 +201,8 @@ class ParserInitTest(util.ApacheTest): self.assertEqual(parser.root, self.config_path) def test_root_absolute(self): - from letsencrypt_apache.parser import ApacheParser - with mock.patch("letsencrypt_apache.parser.ApacheParser." + from certbot_apache.parser import ApacheParser + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): parser = ApacheParser( self.aug, os.path.relpath(self.config_path), @@ -211,8 +211,8 @@ class ParserInitTest(util.ApacheTest): self.assertEqual(parser.root, self.config_path) def test_root_no_trailing_slash(self): - from letsencrypt_apache.parser import ApacheParser - with mock.patch("letsencrypt_apache.parser.ApacheParser." + from certbot_apache.parser import ApacheParser + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): parser = ApacheParser( self.aug, self.config_path + os.path.sep, diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/apache2.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/conf-enabled/dummy.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_fnmatch.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_fnmatch.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_fnmatch.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_fnmatch.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf b/certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_variables.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/complex_parsing/test_variables.conf rename to certbot-apache/certbot_apache/tests/testdata/complex_parsing/test_variables.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf similarity index 88% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf index 2fbfc02a8..e659d4b07 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf @@ -16,8 +16,8 @@ # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. - SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem - SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem + SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem + SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem SSLOptions +StdEnvVars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/sites b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/sites similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/default_vhost/sites rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/sites diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/apache2.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/bad_conf_file.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-available/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/other-vhosts-access-log.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/security.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/conf-enabled/serve-cgi-bin.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/envvars diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/authz_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/dav_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/rewrite.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-available/ssl.load diff --git a/letsencrypt-apache/docs/_static/.gitignore b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore similarity index 100% rename from letsencrypt-apache/docs/_static/.gitignore rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/authz_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/dav_svn.load diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/ports.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/000-default.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf similarity index 91% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf index e38fc9f9b..b3147a523 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/letsencrypt.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/certbot.conf @@ -1,8 +1,8 @@ -ServerName letsencrypt.demo +ServerName certbot.demo ServerAdmin webmaster@localhost -DocumentRoot /var/www-letsencrypt-reworld/static/ +DocumentRoot /var/www-certbot-reworld/static/ Options FollowSymLinks AllowOverride None diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf similarity index 88% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf index 5a50c536e..849b42e9f 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl-port-only.conf @@ -12,8 +12,8 @@ # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. - SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem - SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem + SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem + SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf similarity index 89% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf index f1061c928..a3025ae8a 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf @@ -16,8 +16,8 @@ # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. - SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem - SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem + SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem + SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/mod_macro-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/wildcard.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/000-default.conf diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf new file mode 120000 index 000000000..4d08c763f --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/certbot.conf @@ -0,0 +1 @@ +../sites-available/certbot.conf \ No newline at end of file diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/encryption-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/mod_macro-example.conf diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites similarity index 56% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites rename to certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites index 3e73390fd..06bf6a2ae 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites @@ -1,2 +1,2 @@ -sites-available/letsencrypt.conf, letsencrypt.demo +sites-available/certbot.conf, certbot.demo sites-available/encryption-example.conf, encryption-example.demo diff --git a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py similarity index 91% rename from letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py rename to certbot-apache/certbot_apache/tests/tls_sni_01_test.py index 9681bf9fc..17ef92004 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/tls_sni_01_test.py +++ b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py @@ -1,13 +1,13 @@ -"""Test for letsencrypt_apache.tls_sni_01.""" +"""Test for certbot_apache.tls_sni_01.""" import unittest import shutil import mock -from letsencrypt.plugins import common_test +from certbot.plugins import common_test -from letsencrypt_apache import obj -from letsencrypt_apache.tests import util +from certbot_apache import obj +from certbot_apache.tests import util from six.moves import xrange # pylint: disable=redefined-builtin, import-error @@ -25,7 +25,7 @@ class TlsSniPerformTest(util.ApacheTest): self.config_path, self.vhost_path, self.config_dir, self.work_dir) config.config.tls_sni_01_port = 443 - from letsencrypt_apache import tls_sni_01 + from certbot_apache import tls_sni_01 self.sni = tls_sni_01.ApacheTlsSni01(config) def tearDown(self): @@ -37,8 +37,8 @@ class TlsSniPerformTest(util.ApacheTest): resp = self.sni.perform() self.assertEqual(len(resp), 0) - @mock.patch("letsencrypt.le_util.exe_exists") - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") + @mock.patch("certbot.le_util.run_script") def test_perform1(self, _, mock_exists): mock_register = mock.Mock() self.sni.configurator.reverter.register_undo_command = mock_register @@ -80,7 +80,7 @@ class TlsSniPerformTest(util.ApacheTest): # pylint: disable=protected-access self.sni._setup_challenge_cert = mock_setup_cert - with mock.patch("letsencrypt_apache.configurator.ApacheConfigurator.enable_mod"): + with mock.patch("certbot_apache.configurator.ApacheConfigurator.enable_mod"): sni_responses = self.sni.perform() self.assertEqual(mock_setup_cert.call_count, 2) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/util.py b/certbot-apache/certbot_apache/tests/util.py similarity index 87% rename from letsencrypt-apache/letsencrypt_apache/tests/util.py rename to certbot-apache/certbot_apache/tests/util.py index 2fbfd70c6..9fb5dcdfa 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/util.py +++ b/certbot-apache/certbot_apache/tests/util.py @@ -1,4 +1,4 @@ -"""Common utilities for letsencrypt_apache.""" +"""Common utilities for certbot_apache.""" import os import sys import unittest @@ -9,15 +9,15 @@ import zope.component from acme import jose -from letsencrypt.display import util as display_util +from certbot.display import util as display_util -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt.tests import test_util +from certbot.tests import test_util -from letsencrypt_apache import configurator -from letsencrypt_apache import constants -from letsencrypt_apache import obj +from certbot_apache import configurator +from certbot_apache import constants +from certbot_apache import obj class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods @@ -30,7 +30,7 @@ class ApacheTest(unittest.TestCase): # pylint: disable=too-few-public-methods self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( test_dir=test_dir, - pkg="letsencrypt_apache.tests") + pkg="certbot_apache.tests") self.ssl_options = common.setup_ssl_options( self.config_dir, constants.os_constant("MOD_SSL_CONF_SRC"), @@ -66,10 +66,10 @@ class ParserTest(ApacheTest): # pytlint: disable=too-few-public-methods zope.component.provideUtility(display_util.FileDisplay(sys.stdout)) - from letsencrypt_apache.parser import ApacheParser + from certbot_apache.parser import ApacheParser self.aug = augeas.Augeas( flags=augeas.Augeas.NONE | augeas.Augeas.NO_MODL_AUTOLOAD) - with mock.patch("letsencrypt_apache.parser.ApacheParser." + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): self.parser = ApacheParser( self.aug, self.config_path, self.vhost_path) @@ -95,11 +95,11 @@ def get_apache_configurator( in_progress_dir=os.path.join(backups, "IN_PROGRESS"), work_dir=work_dir) - with mock.patch("letsencrypt_apache.configurator.le_util.run_script"): - with mock.patch("letsencrypt_apache.configurator.le_util." + with mock.patch("certbot_apache.configurator.le_util.run_script"): + with mock.patch("certbot_apache.configurator.le_util." "exe_exists") as mock_exe_exists: mock_exe_exists.return_value = True - with mock.patch("letsencrypt_apache.parser.ApacheParser." + with mock.patch("certbot_apache.parser.ApacheParser." "update_runtime_variables"): config = configurator.ApacheConfigurator( config=mock_le_config, @@ -137,10 +137,10 @@ def get_vh_truth(temp_dir, config_name): obj.Addr.fromstring("[::]:80")]), False, True, "ip-172-30-0-17"), obj.VirtualHost( - os.path.join(prefix, "letsencrypt.conf"), - os.path.join(aug_pre, "letsencrypt.conf/VirtualHost"), + os.path.join(prefix, "certbot.conf"), + os.path.join(aug_pre, "certbot.conf/VirtualHost"), set([obj.Addr.fromstring("*:80")]), False, True, - "letsencrypt.demo"), + "certbot.demo"), obj.VirtualHost( os.path.join(prefix, "mod_macro-example.conf"), os.path.join(aug_pre, diff --git a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py b/certbot-apache/certbot_apache/tls_sni_01.py similarity index 98% rename from letsencrypt-apache/letsencrypt_apache/tls_sni_01.py rename to certbot-apache/certbot_apache/tls_sni_01.py index 9316427e5..1236c2eb9 100644 --- a/letsencrypt-apache/letsencrypt_apache/tls_sni_01.py +++ b/certbot-apache/certbot_apache/tls_sni_01.py @@ -3,10 +3,10 @@ import os import logging -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_apache import obj -from letsencrypt_apache import parser +from certbot_apache import obj +from certbot_apache import parser logger = logging.getLogger(__name__) diff --git a/letsencrypt-apache/docs/.gitignore b/certbot-apache/docs/.gitignore similarity index 100% rename from letsencrypt-apache/docs/.gitignore rename to certbot-apache/docs/.gitignore diff --git a/letsencrypt-apache/docs/Makefile b/certbot-apache/docs/Makefile similarity index 96% rename from letsencrypt-apache/docs/Makefile rename to certbot-apache/docs/Makefile index 9bf5154fe..0e611ecec 100644 --- a/letsencrypt-apache/docs/Makefile +++ b/certbot-apache/docs/Makefile @@ -87,9 +87,9 @@ 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 "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-apache.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-apache.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-apache.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ 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 "# mkdir -p $$HOME/.local/share/devhelp/certbot-apache" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-apache" @echo "# devhelp" epub: diff --git a/letsencrypt-apache/docs/_templates/.gitignore b/certbot-apache/docs/_static/.gitignore similarity index 100% rename from letsencrypt-apache/docs/_templates/.gitignore rename to certbot-apache/docs/_static/.gitignore diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore b/certbot-apache/docs/_templates/.gitignore similarity index 100% rename from letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/mods-enabled/.gitignore rename to certbot-apache/docs/_templates/.gitignore diff --git a/letsencrypt-apache/docs/api.rst b/certbot-apache/docs/api.rst similarity index 100% rename from letsencrypt-apache/docs/api.rst rename to certbot-apache/docs/api.rst diff --git a/certbot-apache/docs/api/augeas_configurator.rst b/certbot-apache/docs/api/augeas_configurator.rst new file mode 100644 index 000000000..b47ffbc6b --- /dev/null +++ b/certbot-apache/docs/api/augeas_configurator.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.augeas_configurator` +--------------------------------------------- + +.. automodule:: certbot_apache.augeas_configurator + :members: diff --git a/certbot-apache/docs/api/configurator.rst b/certbot-apache/docs/api/configurator.rst new file mode 100644 index 000000000..8ec266d1a --- /dev/null +++ b/certbot-apache/docs/api/configurator.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.configurator` +-------------------------------------- + +.. automodule:: certbot_apache.configurator + :members: diff --git a/certbot-apache/docs/api/display_ops.rst b/certbot-apache/docs/api/display_ops.rst new file mode 100644 index 000000000..26d3ed3dc --- /dev/null +++ b/certbot-apache/docs/api/display_ops.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.display_ops` +------------------------------------- + +.. automodule:: certbot_apache.display_ops + :members: diff --git a/certbot-apache/docs/api/obj.rst b/certbot-apache/docs/api/obj.rst new file mode 100644 index 000000000..82e58df3f --- /dev/null +++ b/certbot-apache/docs/api/obj.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.obj` +----------------------------- + +.. automodule:: certbot_apache.obj + :members: diff --git a/certbot-apache/docs/api/parser.rst b/certbot-apache/docs/api/parser.rst new file mode 100644 index 000000000..3427735be --- /dev/null +++ b/certbot-apache/docs/api/parser.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.parser` +-------------------------------- + +.. automodule:: certbot_apache.parser + :members: diff --git a/certbot-apache/docs/api/tls_sni_01.rst b/certbot-apache/docs/api/tls_sni_01.rst new file mode 100644 index 000000000..3ecd0a365 --- /dev/null +++ b/certbot-apache/docs/api/tls_sni_01.rst @@ -0,0 +1,5 @@ +:mod:`certbot_apache.tls_sni_01` +------------------------------------ + +.. automodule:: certbot_apache.tls_sni_01 + :members: diff --git a/letsencrypt-apache/docs/conf.py b/certbot-apache/docs/conf.py similarity index 94% rename from letsencrypt-apache/docs/conf.py rename to certbot-apache/docs/conf.py index aa58038cd..b7faa7b26 100644 --- a/letsencrypt-apache/docs/conf.py +++ b/certbot-apache/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letsencrypt-apache documentation build configuration file, created by +# certbot-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 @@ -65,7 +65,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letsencrypt-apache' +project = u'certbot-apache' copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' @@ -227,7 +227,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letsencrypt-apachedoc' +htmlhelp_basename = 'certbot-apachedoc' # -- Options for LaTeX output --------------------------------------------- @@ -249,7 +249,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letsencrypt-apache.tex', u'letsencrypt-apache Documentation', + (master_doc, 'certbot-apache.tex', u'certbot-apache Documentation', u'Let\'s Encrypt Project', 'manual'), ] @@ -279,7 +279,7 @@ latex_documents = [ # 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', + (master_doc, 'certbot-apache', u'certbot-apache Documentation', [author], 1) ] @@ -293,8 +293,8 @@ man_pages = [ # (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.', + (master_doc, 'certbot-apache', u'certbot-apache Documentation', + author, 'certbot-apache', 'One line description of project.', 'Miscellaneous'), ] @@ -314,5 +314,5 @@ texinfo_documents = [ 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), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-apache/docs/index.rst b/certbot-apache/docs/index.rst similarity index 74% rename from letsencrypt-apache/docs/index.rst rename to certbot-apache/docs/index.rst index f968ccbef..bfe4d245c 100644 --- a/letsencrypt-apache/docs/index.rst +++ b/certbot-apache/docs/index.rst @@ -1,9 +1,9 @@ -.. letsencrypt-apache documentation master file, created by +.. certbot-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! +Welcome to certbot-apache's documentation! ============================================== Contents: @@ -18,7 +18,7 @@ Contents: api -.. automodule:: letsencrypt_apache +.. automodule:: certbot_apache :members: diff --git a/letsencrypt-apache/docs/make.bat b/certbot-apache/docs/make.bat similarity index 97% rename from letsencrypt-apache/docs/make.bat rename to certbot-apache/docs/make.bat index 62a54fd2c..3a7818940 100644 --- a/letsencrypt-apache/docs/make.bat +++ b/certbot-apache/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "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.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-apache.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-apache.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-apache.ghc goto end ) diff --git a/letsencrypt-apache/readthedocs.org.requirements.txt b/certbot-apache/readthedocs.org.requirements.txt similarity index 94% rename from letsencrypt-apache/readthedocs.org.requirements.txt rename to certbot-apache/readthedocs.org.requirements.txt index 7855b5ce2..fe30ab1dc 100644 --- a/letsencrypt-apache/readthedocs.org.requirements.txt +++ b/certbot-apache/readthedocs.org.requirements.txt @@ -9,4 +9,4 @@ -e acme -e . --e letsencrypt-apache[docs] +-e certbot-apache[docs] diff --git a/letsencrypt-apache/setup.py b/certbot-apache/setup.py similarity index 89% rename from letsencrypt-apache/setup.py rename to certbot-apache/setup.py index c2ec7dabd..28ad4cbda 100644 --- a/letsencrypt-apache/setup.py +++ b/certbot-apache/setup.py @@ -9,7 +9,7 @@ version = '0.6.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme=={0}'.format(version), - 'letsencrypt=={0}'.format(version), + 'certbot=={0}'.format(version), 'python-augeas', # For pkg_resources. >=1.0 so pip resolves it to a version cryptography # will tolerate; see #2599: @@ -29,7 +29,7 @@ docs_extras = [ ] setup( - name='letsencrypt-apache', + name='certbot-apache', version=version, description="Apache plugin for Let's Encrypt client", url='https://github.com/letsencrypt/letsencrypt', @@ -61,9 +61,9 @@ setup( 'docs': docs_extras, }, entry_points={ - 'letsencrypt.plugins': [ - 'apache = letsencrypt_apache.configurator:ApacheConfigurator', + 'certbot.plugins': [ + 'apache = certbot_apache.configurator:ApacheConfigurator', ], }, - test_suite='letsencrypt_apache', + test_suite='certbot_apache', ) diff --git a/letsencrypt-apache/MANIFEST.in b/letsencrypt-apache/MANIFEST.in deleted file mode 100644 index bdb67199f..000000000 --- a/letsencrypt-apache/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include LICENSE.txt -include README.rst -recursive-include docs * -recursive-include letsencrypt_apache/tests/testdata * -include letsencrypt_apache/centos-options-ssl-apache.conf -include letsencrypt_apache/options-ssl-apache.conf -recursive-include letsencrypt_apache/augeas_lens *.aug diff --git a/letsencrypt-apache/docs/api/augeas_configurator.rst b/letsencrypt-apache/docs/api/augeas_configurator.rst deleted file mode 100644 index 3b1821e3d..000000000 --- a/letsencrypt-apache/docs/api/augeas_configurator.rst +++ /dev/null @@ -1,5 +0,0 @@ -: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 deleted file mode 100644 index 2ed613286..000000000 --- a/letsencrypt-apache/docs/api/configurator.rst +++ /dev/null @@ -1,5 +0,0 @@ -: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 deleted file mode 100644 index 59ff9d15e..000000000 --- a/letsencrypt-apache/docs/api/display_ops.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.display_ops` -------------------------------------- - -.. automodule:: letsencrypt_apache.display_ops - :members: diff --git a/letsencrypt-apache/docs/api/obj.rst b/letsencrypt-apache/docs/api/obj.rst deleted file mode 100644 index 969293ca1..000000000 --- a/letsencrypt-apache/docs/api/obj.rst +++ /dev/null @@ -1,5 +0,0 @@ -: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 deleted file mode 100644 index 0c998e06c..000000000 --- a/letsencrypt-apache/docs/api/parser.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.parser` --------------------------------- - -.. automodule:: letsencrypt_apache.parser - :members: diff --git a/letsencrypt-apache/docs/api/tls_sni_01.rst b/letsencrypt-apache/docs/api/tls_sni_01.rst deleted file mode 100644 index 2c11a3394..000000000 --- a/letsencrypt-apache/docs/api/tls_sni_01.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_apache.tls_sni_01` ------------------------------------- - -.. automodule:: letsencrypt_apache.tls_sni_01 - :members: diff --git a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt b/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt deleted file mode 100644 index b51956b0c..000000000 --- a/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/NEEDED.txt +++ /dev/null @@ -1,6 +0,0 @@ -Issues for which some kind of test case should be constructable, but we do not -currently have one: - -https://github.com/letsencrypt/letsencrypt/issues/1213 -https://github.com/letsencrypt/letsencrypt/issues/1602 - diff --git a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf b/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf deleted file mode 120000 index f31102913..000000000 --- a/letsencrypt-apache/letsencrypt_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/letsencrypt.conf +++ /dev/null @@ -1 +0,0 @@ -../sites-available/letsencrypt.conf \ No newline at end of file diff --git a/tox.ini b/tox.ini index 8f16b71d1..5d1bc5801 100644 --- a/tox.ini +++ b/tox.ini @@ -17,8 +17,8 @@ commands = nosetests -v acme pip install -e .[dev] nosetests -v certbot - pip install -e letsencrypt-apache - nosetests -v letsencrypt_apache + pip install -e certbot-apache + nosetests -v certbot_apache pip install -e letsencrypt-nginx nosetests -v letsencrypt_nginx pip install -e letshelp-letsencrypt @@ -54,7 +54,7 @@ commands = [testenv:cover] basepython = python2.7 commands = - pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e letsencrypt-nginx -e letshelp-letsencrypt ./tox.cover.sh [testenv:lint] @@ -64,11 +64,11 @@ basepython = python2.7 # duplicate code checking; if one of the commands fails, others will # continue, but tox return code will reflect previous error commands = - pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt ./pep8.travis.sh pylint --rcfile=.pylintrc certbot pylint --rcfile=acme/.pylintrc acme/acme - pylint --rcfile=.pylintrc letsencrypt-apache/letsencrypt_apache + pylint --rcfile=.pylintrc certbot-apache/certbot_apache pylint --rcfile=.pylintrc letsencrypt-nginx/letsencrypt_nginx pylint --rcfile=.pylintrc letsencrypt-compatibility-test/letsencrypt_compatibility_test pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt @@ -76,8 +76,8 @@ commands = [testenv:apacheconftest] #basepython = python2.7 commands = - pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt - {toxinidir}/letsencrypt-apache/letsencrypt_apache/tests/apache-conf-files/apache-conf-test --debian-modules + pip install -e acme -e .[dev] -e certbot-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + {toxinidir}/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test --debian-modules [testenv:le_auto] From 755dc2f08d321fd89b20f64819a64898a1744415 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:36:53 -0700 Subject: [PATCH 212/317] s/Let's Encrypt/Certbot certbot-apache --- certbot-apache/README.rst | 2 +- certbot-apache/certbot_apache/__init__.py | 2 +- certbot-apache/certbot_apache/augeas_lens/README | 2 +- certbot-apache/certbot_apache/configurator.py | 4 ++-- certbot-apache/certbot_apache/tests/__init__.py | 2 +- .../default_vhost/apache2/sites-available/000-default.conf | 2 +- certbot-apache/setup.py | 4 ++-- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/certbot-apache/README.rst b/certbot-apache/README.rst index 3505fd594..96a6ff8ae 100644 --- a/certbot-apache/README.rst +++ b/certbot-apache/README.rst @@ -1 +1 @@ -Apache plugin for Let's Encrypt client +Apache plugin for Certbot diff --git a/certbot-apache/certbot_apache/__init__.py b/certbot-apache/certbot_apache/__init__.py index c0d1e0d52..9c195ccc7 100644 --- a/certbot-apache/certbot_apache/__init__.py +++ b/certbot-apache/certbot_apache/__init__.py @@ -1 +1 @@ -"""Let's Encrypt Apache plugin.""" +"""Certbot Apache plugin.""" diff --git a/certbot-apache/certbot_apache/augeas_lens/README b/certbot-apache/certbot_apache/augeas_lens/README index f801efd43..bf9161f93 100644 --- a/certbot-apache/certbot_apache/augeas_lens/README +++ b/certbot-apache/certbot_apache/augeas_lens/README @@ -1,2 +1,2 @@ -Let's Encrypt includes the very latest Augeas lenses in order to ship bug fixes +Certbot includes the very latest Augeas lenses in order to ship bug fixes to Apache configuration handling bugs as quickly as possible diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 5681373b6..0873edd24 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -1154,7 +1154,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): for matches in rewrite_args_dict.values(): if [self.aug.get(x) for x in matches] in redirect_args: raise errors.PluginEnhancementAlreadyPresent( - "Let's Encrypt has already enabled redirection") + "Certbot has already enabled redirection") def _is_rewrite_exists(self, vhost): """Checks if there exists a RewriteRule directive in vhost @@ -1635,7 +1635,7 @@ def get_file_path(vhost_path): def install_ssl_options_conf(options_ssl): """ - Copy Let's Encrypt's SSL options file into the system's config dir if + Copy Certbot's SSL options file into the system's config dir if required. """ # XXX if we ever try to enforce a local privilege boundary (eg, running diff --git a/certbot-apache/certbot_apache/tests/__init__.py b/certbot-apache/certbot_apache/tests/__init__.py index 2c0849a3d..7e7d39fa4 100644 --- a/certbot-apache/certbot_apache/tests/__init__.py +++ b/certbot-apache/certbot_apache/tests/__init__.py @@ -1 +1 @@ -"""Let's Encrypt Apache Tests""" +"""Certbot Apache Tests""" diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf index 8da335d35..d81fe132d 100644 --- a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf @@ -1,5 +1,5 @@ - # How well does Let's Encrypt work without a ServerName/Alias? + # How well does Certbot work without a ServerName/Alias? ServerAdmin webmaster@localhost DocumentRoot /var/www/html diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 28ad4cbda..89373800c 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -31,9 +31,9 @@ docs_extras = [ setup( name='certbot-apache', version=version, - description="Apache plugin for Let's Encrypt client", + description="Apache plugin for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Electronic Frontier Foundation", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ From e3aba30ec48c67120cc536cf2ec9ff922fbe9ae9 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:37:38 -0700 Subject: [PATCH 213/317] Change Certbot author and description --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 022f3ffb3..a21c43946 100644 --- a/setup.py +++ b/setup.py @@ -87,10 +87,10 @@ docs_extras = [ setup( name='certbot', version=version, - description="Let's Encrypt client", + description="ACME client", long_description=readme, # later: + '\n\n' + changes url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Electronic Frontier Foundation", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ From a43fac3277fbadaea0311e915efa03aa15f94542 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:45:54 -0700 Subject: [PATCH 214/317] s/letsencrypt/certbot letsencrypt-nginx tests --- .../LICENSE.txt | 0 certbot-nginx/MANIFEST.in | 5 +++ .../README.rst | 0 .../certbot_nginx}/__init__.py | 0 .../certbot_nginx}/configurator.py | 38 ++++++++--------- .../certbot_nginx}/constants.py | 2 +- .../certbot_nginx}/nginxparser.py | 0 .../certbot_nginx}/obj.py | 2 +- .../certbot_nginx}/options-ssl-nginx.conf | 0 .../certbot_nginx}/parser.py | 8 ++-- .../certbot_nginx}/tests/__init__.py | 0 .../certbot_nginx}/tests/configurator_test.py | 42 +++++++++---------- .../certbot_nginx}/tests/nginxparser_test.py | 6 +-- .../certbot_nginx}/tests/obj_test.py | 16 +++---- .../certbot_nginx}/tests/parser_test.py | 12 +++--- .../tests/testdata/etc_nginx/broken.conf | 0 .../tests/testdata/etc_nginx/edge_cases.conf | 0 .../tests/testdata/etc_nginx/foo.conf | 0 .../tests/testdata/etc_nginx/mime.types | 0 .../etc_nginx/minimalistic_comments.conf | 0 .../etc_nginx/minimalistic_comments.new.conf | 0 .../tests/testdata/etc_nginx/nginx.conf | 0 .../tests/testdata/etc_nginx/nginx.new.conf | 0 .../tests/testdata/etc_nginx/server.conf | 0 .../testdata/etc_nginx/sites-enabled/default | 0 .../etc_nginx/sites-enabled/example.com | 0 .../default_vhost/nginx/fastcgi_params | 0 .../default_vhost/nginx/koi-utf | 0 .../default_vhost/nginx/koi-win | 0 .../default_vhost/nginx/mime.types | 0 .../default_vhost/nginx/naxsi-ui.conf.1.4.1 | 0 .../default_vhost/nginx/naxsi.rules | 0 .../default_vhost/nginx/naxsi_core.rules | 0 .../default_vhost/nginx/nginx.conf | 0 .../default_vhost/nginx/proxy_params | 0 .../default_vhost/nginx/scgi_params | 0 .../nginx/sites-available/default | 0 .../default_vhost/nginx/sites-enabled/default | 0 .../default_vhost/nginx/uwsgi_params | 0 .../default_vhost/nginx/win-utf | 0 .../certbot_nginx}/tests/tls_sni_01_test.py | 20 ++++----- .../certbot_nginx}/tests/util.py | 20 ++++----- .../certbot_nginx}/tls_sni_01.py | 16 +++---- .../docs/.gitignore | 0 .../docs/Makefile | 8 ++-- .../docs/_static/.gitignore | 0 .../docs/_templates/.gitignore | 0 .../docs/api.rst | 0 certbot-nginx/docs/api/nginxparser.rst | 5 +++ certbot-nginx/docs/api/obj.rst | 5 +++ certbot-nginx/docs/api/parser.rst | 5 +++ certbot-nginx/docs/api/tls_sni_01.rst | 5 +++ .../docs/conf.py | 16 +++---- .../docs/index.rst | 6 +-- .../docs/make.bat | 4 +- .../readthedocs.org.requirements.txt | 2 +- {letsencrypt-nginx => certbot-nginx}/setup.py | 10 ++--- .../tests/boulder-integration.conf.sh | 0 .../tests/boulder-integration.sh | 8 ++-- letsencrypt-nginx/MANIFEST.in | 5 --- letsencrypt-nginx/docs/api/nginxparser.rst | 5 --- letsencrypt-nginx/docs/api/obj.rst | 5 --- letsencrypt-nginx/docs/api/parser.rst | 5 --- letsencrypt-nginx/docs/api/tls_sni_01.rst | 5 --- tests/display.py | 4 +- tests/integration/_common.sh | 8 ++-- tests/travis-integration.sh | 6 +-- tox.ini | 12 +++--- 68 files changed, 158 insertions(+), 158 deletions(-) rename {letsencrypt-nginx => certbot-nginx}/LICENSE.txt (100%) create mode 100644 certbot-nginx/MANIFEST.in rename {letsencrypt-nginx => certbot-nginx}/README.rst (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/__init__.py (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/configurator.py (96%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/constants.py (88%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/nginxparser.py (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/obj.py (99%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/options-ssl-nginx.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/parser.py (99%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/__init__.py (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/configurator_test.py (93%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/nginxparser_test.py (98%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/obj_test.py (89%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/parser_test.py (98%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/broken.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/edge_cases.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/foo.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/mime.types (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/minimalistic_comments.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/minimalistic_comments.new.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/nginx.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/nginx.new.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/server.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/sites-enabled/default (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/sites-enabled/example.com (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf (100%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/tls_sni_01_test.py (92%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tests/util.py (86%) rename {letsencrypt-nginx/letsencrypt_nginx => certbot-nginx/certbot_nginx}/tls_sni_01.py (92%) rename {letsencrypt-nginx => certbot-nginx}/docs/.gitignore (100%) rename {letsencrypt-nginx => certbot-nginx}/docs/Makefile (96%) rename {letsencrypt-nginx => certbot-nginx}/docs/_static/.gitignore (100%) rename {letsencrypt-nginx => certbot-nginx}/docs/_templates/.gitignore (100%) rename {letsencrypt-nginx => certbot-nginx}/docs/api.rst (100%) create mode 100644 certbot-nginx/docs/api/nginxparser.rst create mode 100644 certbot-nginx/docs/api/obj.rst create mode 100644 certbot-nginx/docs/api/parser.rst create mode 100644 certbot-nginx/docs/api/tls_sni_01.rst rename {letsencrypt-nginx => certbot-nginx}/docs/conf.py (94%) rename {letsencrypt-nginx => certbot-nginx}/docs/index.rst (74%) rename {letsencrypt-nginx => certbot-nginx}/docs/make.bat (97%) rename {letsencrypt-nginx => certbot-nginx}/readthedocs.org.requirements.txt (94%) rename {letsencrypt-nginx => certbot-nginx}/setup.py (89%) rename {letsencrypt-nginx => certbot-nginx}/tests/boulder-integration.conf.sh (100%) rename {letsencrypt-nginx => certbot-nginx}/tests/boulder-integration.sh (76%) delete mode 100644 letsencrypt-nginx/MANIFEST.in delete mode 100644 letsencrypt-nginx/docs/api/nginxparser.rst delete mode 100644 letsencrypt-nginx/docs/api/obj.rst delete mode 100644 letsencrypt-nginx/docs/api/parser.rst delete mode 100644 letsencrypt-nginx/docs/api/tls_sni_01.rst diff --git a/letsencrypt-nginx/LICENSE.txt b/certbot-nginx/LICENSE.txt similarity index 100% rename from letsencrypt-nginx/LICENSE.txt rename to certbot-nginx/LICENSE.txt diff --git a/certbot-nginx/MANIFEST.in b/certbot-nginx/MANIFEST.in new file mode 100644 index 000000000..2daca6738 --- /dev/null +++ b/certbot-nginx/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +recursive-include certbot_nginx/tests/testdata * +include certbot_nginx/options-ssl-nginx.conf diff --git a/letsencrypt-nginx/README.rst b/certbot-nginx/README.rst similarity index 100% rename from letsencrypt-nginx/README.rst rename to certbot-nginx/README.rst diff --git a/letsencrypt-nginx/letsencrypt_nginx/__init__.py b/certbot-nginx/certbot_nginx/__init__.py similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/__init__.py rename to certbot-nginx/certbot_nginx/__init__.py diff --git a/letsencrypt-nginx/letsencrypt_nginx/configurator.py b/certbot-nginx/certbot_nginx/configurator.py similarity index 96% rename from letsencrypt-nginx/letsencrypt_nginx/configurator.py rename to certbot-nginx/certbot_nginx/configurator.py index 3a45a2e0e..e402d5c79 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/configurator.py +++ b/certbot-nginx/certbot_nginx/configurator.py @@ -13,19 +13,19 @@ import zope.interface from acme import challenges from acme import crypto_util as acme_crypto_util -from letsencrypt import constants as core_constants -from letsencrypt import crypto_util -from letsencrypt import errors -from letsencrypt import interfaces -from letsencrypt import le_util -from letsencrypt import reverter +from certbot import constants as core_constants +from certbot import crypto_util +from certbot import errors +from certbot import interfaces +from certbot import le_util +from certbot import reverter -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_nginx import constants -from letsencrypt_nginx import tls_sni_01 -from letsencrypt_nginx import obj -from letsencrypt_nginx import parser +from certbot_nginx import constants +from certbot_nginx import tls_sni_01 +from certbot_nginx import obj +from certbot_nginx import parser logger = logging.getLogger(__name__) @@ -41,15 +41,15 @@ class NginxConfigurator(common.Plugin): config files modified by the configurator will lose all their comments. :ivar config: Configuration. - :type config: :class:`~letsencrypt.interfaces.IConfig` + :type config: :class:`~certbot.interfaces.IConfig` :ivar parser: Handles low level parsing - :type parser: :class:`~letsencrypt_nginx.parser` + :type parser: :class:`~certbot_nginx.parser` :ivar str save_notes: Human-readable config change notes :ivar reverter: saves and reverts checkpoints - :type reverter: :class:`letsencrypt.reverter.Reverter` + :type reverter: :class:`certbot.reverter.Reverter` :ivar tup version: version of Nginx @@ -216,7 +216,7 @@ class NginxConfigurator(common.Plugin): :param str target_name: domain name :returns: ssl vhost associated with name - :rtype: :class:`~letsencrypt_nginx.obj.VirtualHost` + :rtype: :class:`~certbot_nginx.obj.VirtualHost` """ vhost = None @@ -333,7 +333,7 @@ class NginxConfigurator(common.Plugin): the existing one? :param vhost: The vhost to add SSL to. - :type vhost: :class:`~letsencrypt_nginx.obj.VirtualHost` + :type vhost: :class:`~certbot_nginx.obj.VirtualHost` """ snakeoil_cert, snakeoil_key = self._get_snakeoil_paths() @@ -372,9 +372,9 @@ class NginxConfigurator(common.Plugin): :param str domain: domain to enhance :param str enhancement: enhancement type defined in - :const:`~letsencrypt.constants.ENHANCEMENTS` + :const:`~certbot.constants.ENHANCEMENTS` :param options: options for the enhancement - See :const:`~letsencrypt.constants.ENHANCEMENTS` + See :const:`~certbot.constants.ENHANCEMENTS` documentation for appropriate parameter. """ @@ -395,7 +395,7 @@ class NginxConfigurator(common.Plugin): .. note:: This function saves the configuration :param vhost: Destination of traffic, an ssl enabled vhost - :type vhost: :class:`~letsencrypt_nginx.obj.VirtualHost` + :type vhost: :class:`~certbot_nginx.obj.VirtualHost` :param unused_options: Not currently used :type unused_options: Not Available diff --git a/letsencrypt-nginx/letsencrypt_nginx/constants.py b/certbot-nginx/certbot_nginx/constants.py similarity index 88% rename from letsencrypt-nginx/letsencrypt_nginx/constants.py rename to certbot-nginx/certbot_nginx/constants.py index 08b205d2a..40ca6c50e 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/constants.py +++ b/certbot-nginx/certbot_nginx/constants.py @@ -13,6 +13,6 @@ MOD_SSL_CONF_DEST = "options-ssl-nginx.conf" """Name of the mod_ssl config file as saved in `IConfig.config_dir`.""" MOD_SSL_CONF_SRC = pkg_resources.resource_filename( - "letsencrypt_nginx", "options-ssl-nginx.conf") + "certbot_nginx", "options-ssl-nginx.conf") """Path to the nginx mod_ssl config file found in the Let's Encrypt distribution.""" diff --git a/letsencrypt-nginx/letsencrypt_nginx/nginxparser.py b/certbot-nginx/certbot_nginx/nginxparser.py similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/nginxparser.py rename to certbot-nginx/certbot_nginx/nginxparser.py diff --git a/letsencrypt-nginx/letsencrypt_nginx/obj.py b/certbot-nginx/certbot_nginx/obj.py similarity index 99% rename from letsencrypt-nginx/letsencrypt_nginx/obj.py rename to certbot-nginx/certbot_nginx/obj.py index 421c676b6..0d1151f39 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/obj.py +++ b/certbot-nginx/certbot_nginx/obj.py @@ -1,7 +1,7 @@ """Module contains classes used by the Nginx Configurator.""" import re -from letsencrypt.plugins import common +from certbot.plugins import common class Addr(common.Addr): diff --git a/letsencrypt-nginx/letsencrypt_nginx/options-ssl-nginx.conf b/certbot-nginx/certbot_nginx/options-ssl-nginx.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/options-ssl-nginx.conf rename to certbot-nginx/certbot_nginx/options-ssl-nginx.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/parser.py b/certbot-nginx/certbot_nginx/parser.py similarity index 99% rename from letsencrypt-nginx/letsencrypt_nginx/parser.py rename to certbot-nginx/certbot_nginx/parser.py index 3b1dd049e..2f08c15d3 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/parser.py +++ b/certbot-nginx/certbot_nginx/parser.py @@ -5,10 +5,10 @@ import os import pyparsing import re -from letsencrypt import errors +from certbot import errors -from letsencrypt_nginx import obj -from letsencrypt_nginx import nginxparser +from certbot_nginx import obj +from certbot_nginx import nginxparser logger = logging.getLogger(__name__) @@ -87,7 +87,7 @@ class NginxParser(object): Technically this is a misnomer because Nginx does not have virtual hosts, it has 'server blocks'. - :returns: List of :class:`~letsencrypt_nginx.obj.VirtualHost` + :returns: List of :class:`~certbot_nginx.obj.VirtualHost` objects found in configuration :rtype: list diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/__init__.py b/certbot-nginx/certbot_nginx/tests/__init__.py similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/__init__.py rename to certbot-nginx/certbot_nginx/tests/__init__.py diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py b/certbot-nginx/certbot_nginx/tests/configurator_test.py similarity index 93% rename from letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py rename to certbot-nginx/certbot_nginx/tests/configurator_test.py index 4d15d6a75..b36802939 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/configurator_test.py +++ b/certbot-nginx/certbot_nginx/tests/configurator_test.py @@ -1,5 +1,5 @@ # pylint: disable=too-many-public-methods -"""Test for letsencrypt_nginx.configurator.""" +"""Test for certbot_nginx.configurator.""" import os import shutil import unittest @@ -10,10 +10,10 @@ import OpenSSL from acme import challenges from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt_nginx.tests import util +from certbot_nginx.tests import util class NginxConfiguratorTest(util.NginxTest): @@ -30,7 +30,7 @@ class NginxConfiguratorTest(util.NginxTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_nginx.configurator.le_util.exe_exists") + @mock.patch("certbot_nginx.configurator.le_util.exe_exists") def test_prepare_no_install(self, mock_exe_exists): mock_exe_exists.return_value = False self.assertRaises( @@ -40,8 +40,8 @@ class NginxConfiguratorTest(util.NginxTest): self.assertEquals((1, 6, 2), self.config.version) self.assertEquals(5, len(self.config.parser.parsed)) - @mock.patch("letsencrypt_nginx.configurator.le_util.exe_exists") - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.le_util.exe_exists") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_prepare_initializes_version(self, mock_popen, mock_exe_exists): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.6.2", @@ -58,7 +58,7 @@ class NginxConfiguratorTest(util.NginxTest): self.config.prepare() self.assertEquals((1, 6, 2), self.config.version) - @mock.patch("letsencrypt_nginx.configurator.socket.gethostbyaddr") + @mock.patch("certbot_nginx.configurator.socket.gethostbyaddr") def test_get_all_names(self, mock_gethostbyaddr): mock_gethostbyaddr.return_value = ('155.225.50.69.nephoscale.net', [], []) names = self.config.get_all_names() @@ -263,8 +263,8 @@ class NginxConfiguratorTest(util.NginxTest): ('/etc/nginx/fullchain.pem', '/etc/nginx/key.pem', nginx_conf), ]), self.config.get_all_certs_keys()) - @mock.patch("letsencrypt_nginx.configurator.tls_sni_01.NginxTlsSni01.perform") - @mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.restart") + @mock.patch("certbot_nginx.configurator.tls_sni_01.NginxTlsSni01.perform") + @mock.patch("certbot_nginx.configurator.NginxConfigurator.restart") def test_perform(self, mock_restart, mock_perform): # Only tests functionality specific to configurator.perform # Note: As more challenges are offered this will have to be expanded @@ -293,7 +293,7 @@ class NginxConfiguratorTest(util.NginxTest): self.assertEqual(responses, expected) self.assertEqual(mock_restart.call_count, 1) - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_get_version(self, mock_popen): mock_popen().communicate.return_value = ( "", "\n".join(["nginx version: nginx/1.4.2", @@ -343,55 +343,55 @@ class NginxConfiguratorTest(util.NginxTest): mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.PluginError, self.config.get_version) - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_nginx_restart(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 0 self.config.restart() - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_nginx_restart_fail(self, mock_popen): mocked = mock_popen() mocked.communicate.return_value = ('', '') mocked.returncode = 1 self.assertRaises(errors.MisconfigurationError, self.config.restart) - @mock.patch("letsencrypt_nginx.configurator.subprocess.Popen") + @mock.patch("certbot_nginx.configurator.subprocess.Popen") def test_no_nginx_start(self, mock_popen): mock_popen.side_effect = OSError("Can't find program") self.assertRaises(errors.MisconfigurationError, self.config.restart) - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test(self, _): self.config.config_test() - @mock.patch("letsencrypt.le_util.run_script") + @mock.patch("certbot.le_util.run_script") def test_config_test_bad_process(self, mock_run_script): mock_run_script.side_effect = errors.SubprocessError self.assertRaises(errors.MisconfigurationError, self.config.config_test) - @mock.patch("letsencrypt.reverter.Reverter.recovery_routine") + @mock.patch("certbot.reverter.Reverter.recovery_routine") def test_recovery_routine_throws_error_from_reverter(self, mock_recovery_routine): mock_recovery_routine.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.recovery_routine) - @mock.patch("letsencrypt.reverter.Reverter.view_config_changes") + @mock.patch("certbot.reverter.Reverter.view_config_changes") def test_view_config_changes_throws_error_from_reverter(self, mock_view_config_changes): mock_view_config_changes.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.view_config_changes) - @mock.patch("letsencrypt.reverter.Reverter.rollback_checkpoints") + @mock.patch("certbot.reverter.Reverter.rollback_checkpoints") def test_rollback_checkpoints_throws_error_from_reverter(self, mock_rollback_checkpoints): mock_rollback_checkpoints.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.rollback_checkpoints) - @mock.patch("letsencrypt.reverter.Reverter.revert_temporary_config") + @mock.patch("certbot.reverter.Reverter.revert_temporary_config") def test_revert_challenge_config_throws_error_from_reverter(self, mock_revert_temporary_config): mock_revert_temporary_config.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.revert_challenge_config) - @mock.patch("letsencrypt.reverter.Reverter.add_to_checkpoint") + @mock.patch("certbot.reverter.Reverter.add_to_checkpoint") def test_save_throws_error_from_reverter(self, mock_add_to_checkpoint): mock_add_to_checkpoint.side_effect = errors.ReverterError("foo") self.assertRaises(errors.PluginError, self.config.save) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/nginxparser_test.py b/certbot-nginx/certbot_nginx/tests/nginxparser_test.py similarity index 98% rename from letsencrypt-nginx/letsencrypt_nginx/tests/nginxparser_test.py rename to certbot-nginx/certbot_nginx/tests/nginxparser_test.py index 2130b4824..80e82c903 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/nginxparser_test.py +++ b/certbot-nginx/certbot_nginx/tests/nginxparser_test.py @@ -1,12 +1,12 @@ -"""Test for letsencrypt_nginx.nginxparser.""" +"""Test for certbot_nginx.nginxparser.""" import operator import unittest from pyparsing import ParseException -from letsencrypt_nginx.nginxparser import ( +from certbot_nginx.nginxparser import ( RawNginxParser, loads, load, dumps, dump) -from letsencrypt_nginx.tests import util +from certbot_nginx.tests import util FIRST = operator.itemgetter(0) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/obj_test.py b/certbot-nginx/certbot_nginx/tests/obj_test.py similarity index 89% rename from letsencrypt-nginx/letsencrypt_nginx/tests/obj_test.py rename to certbot-nginx/certbot_nginx/tests/obj_test.py index e3c22b49d..e7a993d1b 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/obj_test.py +++ b/certbot-nginx/certbot_nginx/tests/obj_test.py @@ -1,11 +1,11 @@ -"""Test the helper objects in letsencrypt_nginx.obj.""" +"""Test the helper objects in certbot_nginx.obj.""" import unittest class AddrTest(unittest.TestCase): """Test the Addr class.""" def setUp(self): - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import Addr self.addr1 = Addr.fromstring("192.168.1.1") self.addr2 = Addr.fromstring("192.168.1.1:* ssl") self.addr3 = Addr.fromstring("192.168.1.1:80") @@ -56,14 +56,14 @@ class AddrTest(unittest.TestCase): self.assertEqual(str(self.addr6), "80 default_server") def test_eq(self): - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import Addr new_addr1 = Addr.fromstring("192.168.1.1 spdy") self.assertEqual(self.addr1, new_addr1) self.assertNotEqual(self.addr1, self.addr2) self.assertFalse(self.addr1 == 3333) def test_set_inclusion(self): - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import Addr set_a = set([self.addr1, self.addr2]) addr1b = Addr.fromstring("192.168.1.1") addr2b = Addr.fromstring("192.168.1.1:* ssl") @@ -75,16 +75,16 @@ class AddrTest(unittest.TestCase): class VirtualHostTest(unittest.TestCase): """Test the VirtualHost class.""" def setUp(self): - from letsencrypt_nginx.obj import VirtualHost - from letsencrypt_nginx.obj import Addr + from certbot_nginx.obj import VirtualHost + from certbot_nginx.obj import Addr self.vhost1 = VirtualHost( "filep", set([Addr.fromstring("localhost")]), False, False, set(['localhost']), []) def test_eq(self): - from letsencrypt_nginx.obj import Addr - from letsencrypt_nginx.obj import VirtualHost + from certbot_nginx.obj import Addr + from certbot_nginx.obj import VirtualHost vhost1b = VirtualHost( "filep", set([Addr.fromstring("localhost blah")]), False, False, diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py b/certbot-nginx/certbot_nginx/tests/parser_test.py similarity index 98% rename from letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py rename to certbot-nginx/certbot_nginx/tests/parser_test.py index b597fcad5..8ac995dfc 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/parser_test.py +++ b/certbot-nginx/certbot_nginx/tests/parser_test.py @@ -1,16 +1,16 @@ -"""Tests for letsencrypt_nginx.parser.""" +"""Tests for certbot_nginx.parser.""" import glob import os import re import shutil import unittest -from letsencrypt import errors +from certbot import errors -from letsencrypt_nginx import nginxparser -from letsencrypt_nginx import obj -from letsencrypt_nginx import parser -from letsencrypt_nginx.tests import util +from certbot_nginx import nginxparser +from certbot_nginx import obj +from certbot_nginx import parser +from certbot_nginx.tests import util class NginxParserTest(util.NginxTest): diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/broken.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/broken.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/broken.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/broken.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/edge_cases.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/edge_cases.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/edge_cases.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/edge_cases.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/foo.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/foo.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/foo.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/foo.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/mime.types b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/mime.types similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/mime.types rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/mime.types diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/minimalistic_comments.new.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.new.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.new.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/nginx.new.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/nginx.new.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/server.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/server.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/server.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/server.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/default rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/default diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/example.com b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/example.com similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/sites-enabled/example.com rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/sites-enabled/example.com diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/fastcgi_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-utf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/koi-win diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/mime.types diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi-ui.conf.1.4.1 diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi.rules diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/naxsi_core.rules diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/nginx.conf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/proxy_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/scgi_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-available/default diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/sites-enabled/default diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/uwsgi_params diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf b/certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf similarity index 100% rename from letsencrypt-nginx/letsencrypt_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf rename to certbot-nginx/certbot_nginx/tests/testdata/etc_nginx/ubuntu_nginx_1_4_6/default_vhost/nginx/win-utf diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/tls_sni_01_test.py b/certbot-nginx/certbot_nginx/tests/tls_sni_01_test.py similarity index 92% rename from letsencrypt-nginx/letsencrypt_nginx/tests/tls_sni_01_test.py rename to certbot-nginx/certbot_nginx/tests/tls_sni_01_test.py index 04fe01bc4..3264d6ed3 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/tls_sni_01_test.py +++ b/certbot-nginx/certbot_nginx/tests/tls_sni_01_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_nginx.tls_sni_01""" +"""Tests for certbot_nginx.tls_sni_01""" import unittest import shutil @@ -6,14 +6,14 @@ import mock from acme import challenges -from letsencrypt import achallenges -from letsencrypt import errors +from certbot import achallenges +from certbot import errors -from letsencrypt.plugins import common_test -from letsencrypt.tests import acme_util +from certbot.plugins import common_test +from certbot.tests import acme_util -from letsencrypt_nginx import obj -from letsencrypt_nginx.tests import util +from certbot_nginx import obj +from certbot_nginx.tests import util class TlsSniPerformTest(util.NginxTest): @@ -47,7 +47,7 @@ class TlsSniPerformTest(util.NginxTest): config = util.get_nginx_configurator( self.config_path, self.config_dir, self.work_dir) - from letsencrypt_nginx import tls_sni_01 + from certbot_nginx import tls_sni_01 self.sni = tls_sni_01.NginxTlsSni01(config) def tearDown(self): @@ -55,7 +55,7 @@ class TlsSniPerformTest(util.NginxTest): shutil.rmtree(self.config_dir) shutil.rmtree(self.work_dir) - @mock.patch("letsencrypt_nginx.configurator" + @mock.patch("certbot_nginx.configurator" ".NginxConfigurator.choose_vhost") def test_perform(self, mock_choose): self.sni.add_chall(self.achalls[1]) @@ -67,7 +67,7 @@ class TlsSniPerformTest(util.NginxTest): responses = self.sni.perform() self.assertEqual([], responses) - @mock.patch("letsencrypt_nginx.configurator.NginxConfigurator.save") + @mock.patch("certbot_nginx.configurator.NginxConfigurator.save") def test_perform1(self, mock_save): self.sni.add_chall(self.achalls[0]) response = self.achalls[0].response(self.account_key) diff --git a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py b/certbot-nginx/certbot_nginx/tests/util.py similarity index 86% rename from letsencrypt-nginx/letsencrypt_nginx/tests/util.py rename to certbot-nginx/certbot_nginx/tests/util.py index 7a16e3738..3c4731700 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tests/util.py +++ b/certbot-nginx/certbot_nginx/tests/util.py @@ -1,4 +1,4 @@ -"""Common utilities for letsencrypt_nginx.""" +"""Common utilities for certbot_nginx.""" import os import pkg_resources import unittest @@ -8,14 +8,14 @@ import zope.component from acme import jose -from letsencrypt import configuration +from certbot import configuration -from letsencrypt.tests import test_util +from certbot.tests import test_util -from letsencrypt.plugins import common +from certbot.plugins import common -from letsencrypt_nginx import constants -from letsencrypt_nginx import configurator +from certbot_nginx import constants +from certbot_nginx import configurator class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods @@ -24,7 +24,7 @@ class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods super(NginxTest, self).setUp() self.temp_dir, self.config_dir, self.work_dir = common.dir_setup( - "etc_nginx", "letsencrypt_nginx.tests") + "etc_nginx", "certbot_nginx.tests") self.ssl_options = common.setup_ssl_options( self.config_dir, constants.MOD_SSL_CONF_SRC, @@ -39,7 +39,7 @@ class NginxTest(unittest.TestCase): # pylint: disable=too-few-public-methods def get_data_filename(filename): """Gets the filename of a test data file.""" return pkg_resources.resource_filename( - "letsencrypt_nginx.tests", os.path.join( + "certbot_nginx.tests", os.path.join( "testdata", "etc_nginx", filename)) @@ -49,9 +49,9 @@ def get_nginx_configurator( backups = os.path.join(work_dir, "backups") - with mock.patch("letsencrypt_nginx.configurator.NginxConfigurator." + with mock.patch("certbot_nginx.configurator.NginxConfigurator." "config_test"): - with mock.patch("letsencrypt_nginx.configurator.le_util." + with mock.patch("certbot_nginx.configurator.le_util." "exe_exists") as mock_exe_exists: mock_exe_exists.return_value = True config = configurator.NginxConfigurator( diff --git a/letsencrypt-nginx/letsencrypt_nginx/tls_sni_01.py b/certbot-nginx/certbot_nginx/tls_sni_01.py similarity index 92% rename from letsencrypt-nginx/letsencrypt_nginx/tls_sni_01.py rename to certbot-nginx/certbot_nginx/tls_sni_01.py index e59281c4c..e4c5d31a6 100644 --- a/letsencrypt-nginx/letsencrypt_nginx/tls_sni_01.py +++ b/certbot-nginx/certbot_nginx/tls_sni_01.py @@ -4,11 +4,11 @@ import itertools import logging import os -from letsencrypt import errors -from letsencrypt.plugins import common +from certbot import errors +from certbot.plugins import common -from letsencrypt_nginx import obj -from letsencrypt_nginx import nginxparser +from certbot_nginx import obj +from certbot_nginx import nginxparser logger = logging.getLogger(__name__) @@ -21,7 +21,7 @@ class NginxTlsSni01(common.TLSSNI01): :type configurator: :class:`~nginx.configurator.NginxConfigurator` :ivar list achalls: Annotated - class:`~letsencrypt.achallenges.KeyAuthorizationAnnotatedChallenge` + class:`~certbot.achallenges.KeyAuthorizationAnnotatedChallenge` challenges :param list indices: Meant to hold indices of challenges in a @@ -39,7 +39,7 @@ class NginxTlsSni01(common.TLSSNI01): def perform(self): """Perform a challenge on Nginx. - :returns: list of :class:`letsencrypt.acme.challenges.TLSSNI01Response` + :returns: list of :class:`certbot.acme.challenges.TLSSNI01Response` :rtype: list """ @@ -83,7 +83,7 @@ class NginxTlsSni01(common.TLSSNI01): """Modifies Nginx config to include challenge server blocks. :param list ll_addrs: list of lists of - :class:`letsencrypt_nginx.obj.Addr` to apply + :class:`certbot_nginx.obj.Addr` to apply :raises .MisconfigurationError: Unable to find a suitable HTTP block in which to include @@ -130,7 +130,7 @@ class NginxTlsSni01(common.TLSSNI01): :param achall: Annotated TLS-SNI-01 challenge :type achall: - :class:`letsencrypt.achallenges.KeyAuthorizationAnnotatedChallenge` + :class:`certbot.achallenges.KeyAuthorizationAnnotatedChallenge` :param list addrs: addresses of challenged domain :class:`list` of type :class:`~nginx.obj.Addr` diff --git a/letsencrypt-nginx/docs/.gitignore b/certbot-nginx/docs/.gitignore similarity index 100% rename from letsencrypt-nginx/docs/.gitignore rename to certbot-nginx/docs/.gitignore diff --git a/letsencrypt-nginx/docs/Makefile b/certbot-nginx/docs/Makefile similarity index 96% rename from letsencrypt-nginx/docs/Makefile rename to certbot-nginx/docs/Makefile index 3a3828235..0bd88a347 100644 --- a/letsencrypt-nginx/docs/Makefile +++ b/certbot-nginx/docs/Makefile @@ -87,9 +87,9 @@ 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 "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-nginx.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-nginx.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-nginx.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ 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 "# mkdir -p $$HOME/.local/share/devhelp/certbot-nginx" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-nginx" @echo "# devhelp" epub: diff --git a/letsencrypt-nginx/docs/_static/.gitignore b/certbot-nginx/docs/_static/.gitignore similarity index 100% rename from letsencrypt-nginx/docs/_static/.gitignore rename to certbot-nginx/docs/_static/.gitignore diff --git a/letsencrypt-nginx/docs/_templates/.gitignore b/certbot-nginx/docs/_templates/.gitignore similarity index 100% rename from letsencrypt-nginx/docs/_templates/.gitignore rename to certbot-nginx/docs/_templates/.gitignore diff --git a/letsencrypt-nginx/docs/api.rst b/certbot-nginx/docs/api.rst similarity index 100% rename from letsencrypt-nginx/docs/api.rst rename to certbot-nginx/docs/api.rst diff --git a/certbot-nginx/docs/api/nginxparser.rst b/certbot-nginx/docs/api/nginxparser.rst new file mode 100644 index 000000000..6a3be5247 --- /dev/null +++ b/certbot-nginx/docs/api/nginxparser.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.nginxparser` +------------------------------------ + +.. automodule:: certbot_nginx.nginxparser + :members: diff --git a/certbot-nginx/docs/api/obj.rst b/certbot-nginx/docs/api/obj.rst new file mode 100644 index 000000000..a2c94037b --- /dev/null +++ b/certbot-nginx/docs/api/obj.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.obj` +---------------------------- + +.. automodule:: certbot_nginx.obj + :members: diff --git a/certbot-nginx/docs/api/parser.rst b/certbot-nginx/docs/api/parser.rst new file mode 100644 index 000000000..0149f99cb --- /dev/null +++ b/certbot-nginx/docs/api/parser.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.parser` +------------------------------- + +.. automodule:: certbot_nginx.parser + :members: diff --git a/certbot-nginx/docs/api/tls_sni_01.rst b/certbot-nginx/docs/api/tls_sni_01.rst new file mode 100644 index 000000000..5074f63d9 --- /dev/null +++ b/certbot-nginx/docs/api/tls_sni_01.rst @@ -0,0 +1,5 @@ +:mod:`certbot_nginx.tls_sni_01` +----------------------------------- + +.. automodule:: certbot_nginx.tls_sni_01 + :members: diff --git a/letsencrypt-nginx/docs/conf.py b/certbot-nginx/docs/conf.py similarity index 94% rename from letsencrypt-nginx/docs/conf.py rename to certbot-nginx/docs/conf.py index 14713a4b2..fa00e6503 100644 --- a/letsencrypt-nginx/docs/conf.py +++ b/certbot-nginx/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letsencrypt-nginx documentation build configuration file, created by +# certbot-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 @@ -58,7 +58,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letsencrypt-nginx' +project = u'certbot-nginx' copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' @@ -220,7 +220,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letsencrypt-nginxdoc' +htmlhelp_basename = 'certbot-nginxdoc' # -- Options for LaTeX output --------------------------------------------- @@ -242,7 +242,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letsencrypt-nginx.tex', u'letsencrypt-nginx Documentation', + (master_doc, 'certbot-nginx.tex', u'certbot-nginx Documentation', u'Let\'s Encrypt Project', 'manual'), ] @@ -272,7 +272,7 @@ latex_documents = [ # 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', + (master_doc, 'certbot-nginx', u'certbot-nginx Documentation', [author], 1) ] @@ -286,8 +286,8 @@ man_pages = [ # (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.', + (master_doc, 'certbot-nginx', u'certbot-nginx Documentation', + author, 'certbot-nginx', 'One line description of project.', 'Miscellaneous'), ] @@ -307,5 +307,5 @@ texinfo_documents = [ 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), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-nginx/docs/index.rst b/certbot-nginx/docs/index.rst similarity index 74% rename from letsencrypt-nginx/docs/index.rst rename to certbot-nginx/docs/index.rst index e4f8f715f..488a7ab9c 100644 --- a/letsencrypt-nginx/docs/index.rst +++ b/certbot-nginx/docs/index.rst @@ -1,9 +1,9 @@ -.. letsencrypt-nginx documentation master file, created by +.. certbot-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! +Welcome to certbot-nginx's documentation! ============================================= Contents: @@ -18,7 +18,7 @@ Contents: api -.. automodule:: letsencrypt_nginx +.. automodule:: certbot_nginx :members: diff --git a/letsencrypt-nginx/docs/make.bat b/certbot-nginx/docs/make.bat similarity index 97% rename from letsencrypt-nginx/docs/make.bat rename to certbot-nginx/docs/make.bat index eb19a3fb5..b12255d4c 100644 --- a/letsencrypt-nginx/docs/make.bat +++ b/certbot-nginx/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "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.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-nginx.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-nginx.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-nginx.ghc goto end ) diff --git a/letsencrypt-nginx/readthedocs.org.requirements.txt b/certbot-nginx/readthedocs.org.requirements.txt similarity index 94% rename from letsencrypt-nginx/readthedocs.org.requirements.txt rename to certbot-nginx/readthedocs.org.requirements.txt index 3b55df408..ca5f33363 100644 --- a/letsencrypt-nginx/readthedocs.org.requirements.txt +++ b/certbot-nginx/readthedocs.org.requirements.txt @@ -9,4 +9,4 @@ -e acme -e . --e letsencrypt-nginx[docs] +-e certbot-nginx[docs] diff --git a/letsencrypt-nginx/setup.py b/certbot-nginx/setup.py similarity index 89% rename from letsencrypt-nginx/setup.py rename to certbot-nginx/setup.py index 54b69adc3..4fdfcd858 100644 --- a/letsencrypt-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -9,7 +9,7 @@ version = '0.6.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ 'acme=={0}'.format(version), - 'letsencrypt=={0}'.format(version), + 'certbot=={0}'.format(version), 'PyOpenSSL', 'pyparsing>=1.5.5', # Python3 support; perhaps unnecessary? # For pkg_resources. >=1.0 so pip resolves it to a version cryptography @@ -29,7 +29,7 @@ docs_extras = [ ] setup( - name='letsencrypt-nginx', + name='certbot-nginx', version=version, description="Nginx plugin for Let's Encrypt client", url='https://github.com/letsencrypt/letsencrypt', @@ -61,9 +61,9 @@ setup( 'docs': docs_extras, }, entry_points={ - 'letsencrypt.plugins': [ - 'nginx = letsencrypt_nginx.configurator:NginxConfigurator', + 'certbot.plugins': [ + 'nginx = certbot_nginx.configurator:NginxConfigurator', ], }, - test_suite='letsencrypt_nginx', + test_suite='certbot_nginx', ) diff --git a/letsencrypt-nginx/tests/boulder-integration.conf.sh b/certbot-nginx/tests/boulder-integration.conf.sh similarity index 100% rename from letsencrypt-nginx/tests/boulder-integration.conf.sh rename to certbot-nginx/tests/boulder-integration.conf.sh diff --git a/letsencrypt-nginx/tests/boulder-integration.sh b/certbot-nginx/tests/boulder-integration.sh similarity index 76% rename from letsencrypt-nginx/tests/boulder-integration.sh rename to certbot-nginx/tests/boulder-integration.sh index 3cbe9f6b9..bd35aee21 100755 --- a/letsencrypt-nginx/tests/boulder-integration.sh +++ b/certbot-nginx/tests/boulder-integration.sh @@ -6,19 +6,19 @@ export PATH="/usr/sbin:$PATH" # /usr/sbin/nginx nginx_root="$root/nginx" mkdir $nginx_root -root="$nginx_root" ./letsencrypt-nginx/tests/boulder-integration.conf.sh > $nginx_root/nginx.conf +root="$nginx_root" ./certbot-nginx/tests/boulder-integration.conf.sh > $nginx_root/nginx.conf killall nginx || true nginx -c $nginx_root/nginx.conf -letsencrypt_test_nginx () { - letsencrypt_test \ +certbot_test_nginx () { + certbot_test \ --configurator nginx \ --nginx-server-root $nginx_root \ "$@" } -letsencrypt_test_nginx --domains nginx.wtf run +certbot_test_nginx --domains nginx.wtf run echo | openssl s_client -connect localhost:5001 \ | openssl x509 -out $root/nginx.pem diff -q $root/nginx.pem $root/conf/live/nginx.wtf/cert.pem diff --git a/letsencrypt-nginx/MANIFEST.in b/letsencrypt-nginx/MANIFEST.in deleted file mode 100644 index 912d624d9..000000000 --- a/letsencrypt-nginx/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -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/letsencrypt-nginx/docs/api/nginxparser.rst b/letsencrypt-nginx/docs/api/nginxparser.rst deleted file mode 100644 index e55bda0b1..000000000 --- a/letsencrypt-nginx/docs/api/nginxparser.rst +++ /dev/null @@ -1,5 +0,0 @@ -: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 deleted file mode 100644 index 418b87cf7..000000000 --- a/letsencrypt-nginx/docs/api/obj.rst +++ /dev/null @@ -1,5 +0,0 @@ -: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 deleted file mode 100644 index 6582263ef..000000000 --- a/letsencrypt-nginx/docs/api/parser.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_nginx.parser` -------------------------------- - -.. automodule:: letsencrypt_nginx.parser - :members: diff --git a/letsencrypt-nginx/docs/api/tls_sni_01.rst b/letsencrypt-nginx/docs/api/tls_sni_01.rst deleted file mode 100644 index f9f584b0c..000000000 --- a/letsencrypt-nginx/docs/api/tls_sni_01.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt_nginx.tls_sni_01` ------------------------------------ - -.. automodule:: letsencrypt_nginx.tls_sni_01 - :members: diff --git a/tests/display.py b/tests/display.py index dff56e42e..ecb7c279b 100644 --- a/tests/display.py +++ b/tests/display.py @@ -1,8 +1,8 @@ """Manual test of display functions.""" import sys -from letsencrypt.display import util -from letsencrypt.tests.display import util_test +from certbot.display import util +from certbot.tests.display import util_test def test_visual(displayer, choices): diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index e86d087cb..8992a18c0 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -11,14 +11,14 @@ store_flags="--config-dir $root/conf --work-dir $root/work" store_flags="$store_flags --logs-dir $root/logs" export root store_flags -letsencrypt_test () { - letsencrypt_test_no_force_renew \ +certbot_test () { + certbot_test_no_force_renew \ --renew-by-default \ "$@" } -letsencrypt_test_no_force_renew () { - letsencrypt \ +certbot_test_no_force_renew () { + certbot \ --server "${SERVER:-http://localhost:4000/directory}" \ --no-verify-ssl \ --tls-sni-01-port 5001 \ diff --git a/tests/travis-integration.sh b/tests/travis-integration.sh index 3b507bb86..1b51f0980 100755 --- a/tests/travis-integration.sh +++ b/tests/travis-integration.sh @@ -11,9 +11,9 @@ export LETSENCRYPT_PATH=`pwd` cd $GOPATH/src/github.com/letsencrypt/boulder/ # boulder's integration-test.py has code that knows to start and wait for the -# boulder processes to start reliably and then will run the letsencrypt +# boulder processes to start reliably and then will run the certbot # boulder-interation.sh on its own. The --letsencrypt flag says to run only the -# letsencrypt tests (instead of any other client tests it might run). We're +# certbot tests (instead of any other client tests it might run). We're # going to want to define a more robust interaction point between the boulder -# and letsencrypt tests, but that will be better built off of this. +# and certbot tests, but that will be better built off of this. python test/integration-test.py --letsencrypt diff --git a/tox.ini b/tox.ini index 5d1bc5801..cbed0fac7 100644 --- a/tox.ini +++ b/tox.ini @@ -19,8 +19,8 @@ commands = nosetests -v certbot pip install -e certbot-apache nosetests -v certbot_apache - pip install -e letsencrypt-nginx - nosetests -v letsencrypt_nginx + pip install -e certbot-nginx + nosetests -v certbot_nginx pip install -e letshelp-letsencrypt nosetests -v letshelp_letsencrypt @@ -54,7 +54,7 @@ commands = [testenv:cover] basepython = python2.7 commands = - pip install -e acme[dev] -e .[dev] -e certbot-apache -e letsencrypt-nginx -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e letshelp-letsencrypt ./tox.cover.sh [testenv:lint] @@ -64,19 +64,19 @@ basepython = python2.7 # duplicate code checking; if one of the commands fails, others will # continue, but tox return code will reflect previous error commands = - pip install -e acme[dev] -e .[dev] -e certbot-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt ./pep8.travis.sh pylint --rcfile=.pylintrc certbot pylint --rcfile=acme/.pylintrc acme/acme pylint --rcfile=.pylintrc certbot-apache/certbot_apache - pylint --rcfile=.pylintrc letsencrypt-nginx/letsencrypt_nginx + pylint --rcfile=.pylintrc certbot-nginx/certbot_nginx pylint --rcfile=.pylintrc letsencrypt-compatibility-test/letsencrypt_compatibility_test pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt [testenv:apacheconftest] #basepython = python2.7 commands = - pip install -e acme -e .[dev] -e certbot-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme -e .[dev] -e certbot-apache -e certbot-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt {toxinidir}/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test --debian-modules From 3d248d8a60f085bce920e979a4b80067c0cdd8da Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:49:30 -0700 Subject: [PATCH 215/317] s/Let's Encrypt/Certbot certbot-nginx --- certbot-nginx/README.rst | 2 +- certbot-nginx/certbot_nginx/__init__.py | 2 +- certbot-nginx/certbot_nginx/constants.py | 2 +- certbot-nginx/certbot_nginx/tests/__init__.py | 2 +- certbot-nginx/setup.py | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/certbot-nginx/README.rst b/certbot-nginx/README.rst index ff6d50ce4..69d73ca3c 100644 --- a/certbot-nginx/README.rst +++ b/certbot-nginx/README.rst @@ -1 +1 @@ -Nginx plugin for Let's Encrypt client +Nginx plugin for Certbot diff --git a/certbot-nginx/certbot_nginx/__init__.py b/certbot-nginx/certbot_nginx/__init__.py index 34db9673d..d4491dd9a 100644 --- a/certbot-nginx/certbot_nginx/__init__.py +++ b/certbot-nginx/certbot_nginx/__init__.py @@ -1 +1 @@ -"""Let's Encrypt nginx plugin.""" +"""Certbot nginx plugin.""" diff --git a/certbot-nginx/certbot_nginx/constants.py b/certbot-nginx/certbot_nginx/constants.py index 40ca6c50e..5dde30efc 100644 --- a/certbot-nginx/certbot_nginx/constants.py +++ b/certbot-nginx/certbot_nginx/constants.py @@ -14,5 +14,5 @@ MOD_SSL_CONF_DEST = "options-ssl-nginx.conf" MOD_SSL_CONF_SRC = pkg_resources.resource_filename( "certbot_nginx", "options-ssl-nginx.conf") -"""Path to the nginx mod_ssl config file found in the Let's Encrypt +"""Path to the nginx mod_ssl config file found in the Certbot distribution.""" diff --git a/certbot-nginx/certbot_nginx/tests/__init__.py b/certbot-nginx/certbot_nginx/tests/__init__.py index 157a70759..32ca193d9 100644 --- a/certbot-nginx/certbot_nginx/tests/__init__.py +++ b/certbot-nginx/certbot_nginx/tests/__init__.py @@ -1 +1 @@ -"""Let's Encrypt Nginx Tests""" +"""Certbot Nginx Tests""" diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 4fdfcd858..7c07ff4a0 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -31,9 +31,9 @@ docs_extras = [ setup( name='certbot-nginx', version=version, - description="Nginx plugin for Let's Encrypt client", + description="Nginx plugin for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Electronic Frontier Foundation", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ From 4fab8751b28147325b63c9f9bd78e021bce18303 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:58:21 -0700 Subject: [PATCH 216/317] s/letsencrypt/certbot letsencrypt-compatibility-test --- .../LICENSE.txt | 0 certbot-compatibility-test/MANIFEST.in | 7 +++ .../README.rst | 0 .../certbot_compatibility_test}/__init__.py | 0 .../configurators/__init__.py | 0 .../configurators/apache/Dockerfile | 20 +++++++ .../configurators/apache/__init__.py | 0 .../configurators/apache/a2dismod.sh | 0 .../configurators/apache/a2enmod.sh | 0 .../configurators/apache/apache24.py | 6 +- .../configurators/apache/common.py | 24 ++++---- .../configurators/common.py | 6 +- .../certbot_compatibility_test}/errors.py | 0 .../certbot_compatibility_test}/interfaces.py | 6 +- .../test_driver.py | 14 ++--- .../testdata/configs.tar.gz | Bin .../testdata/empty_cert.pem | 0 .../testdata/rsa1024_key.pem | 0 .../testdata/rsa1024_key2.pem | 0 .../certbot_compatibility_test}/util.py | 6 +- .../certbot_compatibility_test}/validator.py | 2 +- .../validator_test.py | 32 +++++------ .../docs/.gitignore | 0 .../docs/Makefile | 8 +-- .../docs/_static/.gitignore | 0 .../docs/_templates/.gitignore | 0 .../docs/api.rst | 0 certbot-compatibility-test/docs/api/index.rst | 53 ++++++++++++++++++ .../docs/conf.py | 26 ++++----- .../docs/index.rst | 4 +- .../docs/make.bat | 4 +- .../readthedocs.org.requirements.txt | 4 +- .../setup.py | 8 +-- letsencrypt-compatibility-test/MANIFEST.in | 7 --- .../docs/api/index.rst | 53 ------------------ .../configurators/apache/Dockerfile | 20 ------- tox.cover.sh | 6 +- tox.ini | 6 +- 38 files changed, 161 insertions(+), 161 deletions(-) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/LICENSE.txt (100%) create mode 100644 certbot-compatibility-test/MANIFEST.in rename {letsencrypt-compatibility-test => certbot-compatibility-test}/README.rst (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/__init__.py (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/__init__.py (100%) create mode 100644 certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/apache/__init__.py (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/apache/a2dismod.sh (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/apache/a2enmod.sh (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/apache/apache24.py (93%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/apache/common.py (93%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/configurators/common.py (97%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/errors.py (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/interfaces.py (88%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/test_driver.py (97%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/testdata/configs.tar.gz (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/testdata/empty_cert.pem (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/testdata/rsa1024_key.pem (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/testdata/rsa1024_key2.pem (100%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/util.py (92%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/validator.py (98%) rename {letsencrypt-compatibility-test/letsencrypt_compatibility_test => certbot-compatibility-test/certbot_compatibility_test}/validator_test.py (78%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/.gitignore (100%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/Makefile (96%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/_static/.gitignore (100%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/_templates/.gitignore (100%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/api.rst (100%) create mode 100644 certbot-compatibility-test/docs/api/index.rst rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/conf.py (93%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/index.rst (75%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/docs/make.bat (97%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/readthedocs.org.requirements.txt (88%) rename {letsencrypt-compatibility-test => certbot-compatibility-test}/setup.py (87%) delete mode 100644 letsencrypt-compatibility-test/MANIFEST.in delete mode 100644 letsencrypt-compatibility-test/docs/api/index.rst delete mode 100644 letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile diff --git a/letsencrypt-compatibility-test/LICENSE.txt b/certbot-compatibility-test/LICENSE.txt similarity index 100% rename from letsencrypt-compatibility-test/LICENSE.txt rename to certbot-compatibility-test/LICENSE.txt diff --git a/certbot-compatibility-test/MANIFEST.in b/certbot-compatibility-test/MANIFEST.in new file mode 100644 index 000000000..11762538a --- /dev/null +++ b/certbot-compatibility-test/MANIFEST.in @@ -0,0 +1,7 @@ +include LICENSE.txt +include README.rst +recursive-include docs * +include certbot_compatibility_test/configurators/apache/a2enmod.sh +include certbot_compatibility_test/configurators/apache/a2dismod.sh +include certbot_compatibility_test/configurators/apache/Dockerfile +recursive-include certbot_compatibility_test/testdata * diff --git a/letsencrypt-compatibility-test/README.rst b/certbot-compatibility-test/README.rst similarity index 100% rename from letsencrypt-compatibility-test/README.rst rename to certbot-compatibility-test/README.rst diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/__init__.py similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/__init__.py rename to certbot-compatibility-test/certbot_compatibility_test/__init__.py diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/__init__.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile new file mode 100644 index 000000000..ea9bb857f --- /dev/null +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/Dockerfile @@ -0,0 +1,20 @@ +FROM httpd +MAINTAINER Brad Warren + +RUN mkdir /var/run/apache2 + +ENV APACHE_RUN_USER=daemon \ + APACHE_RUN_GROUP=daemon \ + APACHE_PID_FILE=/usr/local/apache2/logs/httpd.pid \ + APACHE_RUN_DIR=/var/run/apache2 \ + APACHE_LOCK_DIR=/var/lock \ + APACHE_LOG_DIR=/usr/local/apache2/logs + +COPY certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2enmod.sh /usr/local/bin/ +COPY certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2dismod.sh /usr/local/bin/ +COPY certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key2.pem /usr/local/apache2/conf/ +COPY certbot-compatibility-test/certbot_compatibility_test/testdata/empty_cert.pem /usr/local/apache2/conf/ + +# Note: this only exposes the port to other docker containers. You +# still have to bind to 443@host at runtime. +EXPOSE 443 diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/__init__.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2dismod.sh b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2dismod.sh similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2dismod.sh rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2dismod.sh diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2enmod.sh b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2enmod.sh similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2enmod.sh rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/a2enmod.sh diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/apache24.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/apache24.py similarity index 93% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/apache24.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/apache24.py index a68f53689..927c329ef 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/apache24.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/apache24.py @@ -2,9 +2,9 @@ import zope.interface -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import interfaces -from letsencrypt_compatibility_test.configurators.apache import common as apache_common +from certbot_compatibility_test import errors +from certbot_compatibility_test import interfaces +from certbot_compatibility_test.configurators.apache import common as apache_common # The docker image doesn't actually have the watchdog module, but unless the diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/common.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py similarity index 93% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/common.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py index d383963a3..f57e0512d 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/common.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/common.py @@ -6,13 +6,13 @@ import subprocess import mock import zope.interface -from letsencrypt import configuration -from letsencrypt import errors as le_errors -from letsencrypt_apache import configurator -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import interfaces -from letsencrypt_compatibility_test import util -from letsencrypt_compatibility_test.configurators import common as configurators_common +from certbot import configuration +from certbot import errors as le_errors +from certbot_apache import configurator +from certbot_compatibility_test import errors +from certbot_compatibility_test import interfaces +from certbot_compatibility_test import util +from certbot_compatibility_test.configurators import common as configurators_common APACHE_VERSION_REGEX = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE) @@ -41,20 +41,20 @@ class Proxy(configurators_common.Proxy): mock_subprocess.Popen = self.popen mock.patch( - "letsencrypt_apache.configurator.subprocess", + "certbot_apache.configurator.subprocess", mock_subprocess).start() mock.patch( - "letsencrypt_apache.parser.subprocess", + "certbot_apache.parser.subprocess", mock_subprocess).start() mock.patch( - "letsencrypt.le_util.subprocess", + "certbot.le_util.subprocess", mock_subprocess).start() mock.patch( - "letsencrypt_apache.configurator.le_util.exe_exists", + "certbot_apache.configurator.le_util.exe_exists", _is_apache_command).start() patch = mock.patch( - "letsencrypt_apache.configurator.display_ops.select_vhost") + "certbot_apache.configurator.display_ops.select_vhost") mock_display = patch.start() mock_display.side_effect = le_errors.PluginError( "Unable to determine vhost") diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/common.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py similarity index 97% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/common.py rename to certbot-compatibility-test/certbot_compatibility_test/configurators/common.py index 7c5e5dfcb..4657883a3 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/common.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/common.py @@ -6,9 +6,9 @@ import tempfile import docker -from letsencrypt import constants -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import util +from certbot import constants +from certbot_compatibility_test import errors +from certbot_compatibility_test import util logger = logging.getLogger(__name__) diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/errors.py b/certbot-compatibility-test/certbot_compatibility_test/errors.py similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/errors.py rename to certbot-compatibility-test/certbot_compatibility_test/errors.py diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/interfaces.py b/certbot-compatibility-test/certbot_compatibility_test/interfaces.py similarity index 88% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/interfaces.py rename to certbot-compatibility-test/certbot_compatibility_test/interfaces.py index fcf7a504f..8f0a6b4c5 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/interfaces.py +++ b/certbot-compatibility-test/certbot_compatibility_test/interfaces.py @@ -1,7 +1,7 @@ """Let's Encrypt compatibility test interfaces""" import zope.interface -import letsencrypt.interfaces +import certbot.interfaces # pylint: disable=no-self-argument,no-method-argument @@ -37,11 +37,11 @@ class IPluginProxy(zope.interface.Interface): """Returns the domain names that can be used in testing""" -class IAuthenticatorProxy(IPluginProxy, letsencrypt.interfaces.IAuthenticator): +class IAuthenticatorProxy(IPluginProxy, certbot.interfaces.IAuthenticator): """Wraps a Let's Encrypt authenticator""" -class IInstallerProxy(IPluginProxy, letsencrypt.interfaces.IInstaller): +class IInstallerProxy(IPluginProxy, certbot.interfaces.IInstaller): """Wraps a Let's Encrypt installer""" def get_all_names_answer(): diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py similarity index 97% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py rename to certbot-compatibility-test/certbot_compatibility_test/test_driver.py index ee679bdb7..6d9ebdada 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/test_driver.py +++ b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py @@ -13,15 +13,15 @@ import OpenSSL from acme import challenges from acme import crypto_util from acme import messages -from letsencrypt import achallenges -from letsencrypt import errors as le_errors -from letsencrypt.tests import acme_util +from certbot import achallenges +from certbot import errors as le_errors +from certbot.tests import acme_util -from letsencrypt_compatibility_test import errors -from letsencrypt_compatibility_test import util -from letsencrypt_compatibility_test import validator +from certbot_compatibility_test import errors +from certbot_compatibility_test import util +from certbot_compatibility_test import validator -from letsencrypt_compatibility_test.configurators.apache import apache24 +from certbot_compatibility_test.configurators.apache import apache24 DESCRIPTION = """ diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/configs.tar.gz b/certbot-compatibility-test/certbot_compatibility_test/testdata/configs.tar.gz similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/configs.tar.gz rename to certbot-compatibility-test/certbot_compatibility_test/testdata/configs.tar.gz diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/empty_cert.pem b/certbot-compatibility-test/certbot_compatibility_test/testdata/empty_cert.pem similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/empty_cert.pem rename to certbot-compatibility-test/certbot_compatibility_test/testdata/empty_cert.pem diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key.pem b/certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key.pem similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key.pem rename to certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key.pem diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key2.pem b/certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key2.pem similarity index 100% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key2.pem rename to certbot-compatibility-test/certbot_compatibility_test/testdata/rsa1024_key2.pem diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py b/certbot-compatibility-test/certbot_compatibility_test/util.py similarity index 92% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py rename to certbot-compatibility-test/certbot_compatibility_test/util.py index b635ee539..8ff9c8dd3 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/util.py +++ b/certbot-compatibility-test/certbot_compatibility_test/util.py @@ -10,9 +10,9 @@ import tarfile from acme import jose from acme import test_util -from letsencrypt import constants +from certbot import constants -from letsencrypt_compatibility_test import errors +from certbot_compatibility_test import errors _KEY_BASE = "rsa1024_key.pem" @@ -26,7 +26,7 @@ def create_le_config(parent_dir): """Sets up LE dirs in parent_dir and returns the config dict""" config = copy.deepcopy(constants.CLI_DEFAULTS) - le_dir = os.path.join(parent_dir, "letsencrypt") + le_dir = os.path.join(parent_dir, "certbot") config["config_dir"] = os.path.join(le_dir, "config") config["work_dir"] = os.path.join(le_dir, "work") config["logs_dir"] = os.path.join(le_dir, "logs_dir") diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py b/certbot-compatibility-test/certbot_compatibility_test/validator.py similarity index 98% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py rename to certbot-compatibility-test/certbot_compatibility_test/validator.py index 90ce108c0..e82b2c049 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator.py +++ b/certbot-compatibility-test/certbot_compatibility_test/validator.py @@ -6,7 +6,7 @@ import zope.interface from acme import crypto_util from acme import errors as acme_errors -from letsencrypt import interfaces +from certbot import interfaces logger = logging.getLogger(__name__) diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py b/certbot-compatibility-test/certbot_compatibility_test/validator_test.py similarity index 78% rename from letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py rename to certbot-compatibility-test/certbot_compatibility_test/validator_test.py index 3a3bbc4b2..d0552a756 100644 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/validator_test.py +++ b/certbot-compatibility-test/certbot_compatibility_test/validator_test.py @@ -1,4 +1,4 @@ -"""Tests for letsencrypt_compatibility_test.validator.""" +"""Tests for certbot_compatibility_test.validator.""" import requests import unittest @@ -6,7 +6,7 @@ import mock import OpenSSL from acme import errors as acme_errors -from letsencrypt_compatibility_test import validator +from certbot_compatibility_test import validator class ValidatorTest(unittest.TestCase): @@ -14,7 +14,7 @@ class ValidatorTest(unittest.TestCase): self.validator = validator.Validator() @mock.patch( - "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") + "certbot_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_success(self, mock_probe_sni): cert = OpenSSL.crypto.X509() mock_probe_sni.return_value = cert @@ -22,7 +22,7 @@ class ValidatorTest(unittest.TestCase): cert, "test.com", "127.0.0.1")) @mock.patch( - "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") + "certbot_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_error(self, mock_probe_sni): cert = OpenSSL.crypto.X509() mock_probe_sni.side_effect = [acme_errors.Error] @@ -30,7 +30,7 @@ class ValidatorTest(unittest.TestCase): cert, "test.com", "127.0.0.1")) @mock.patch( - "letsencrypt_compatibility_test.validator.crypto_util.probe_sni") + "certbot_compatibility_test.validator.crypto_util.probe_sni") def test_certificate_failure(self, mock_probe_sni): cert = OpenSSL.crypto.X509() cert.set_serial_number(1337) @@ -38,67 +38,67 @@ class ValidatorTest(unittest.TestCase): self.assertFalse(self.validator.certificate( cert, "test.com", "127.0.0.1")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_succesful_redirect(self, mock_get_request): mock_get_request.return_value = create_response( 301, {"location": "https://test.com"}) self.assertTrue(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_with_headers(self, mock_get_request): mock_get_request.return_value = create_response( 301, {"location": "https://test.com"}) self.assertTrue(self.validator.redirect( "test.com", headers={"Host": "test.com"})) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_missing_location(self, mock_get_request): mock_get_request.return_value = create_response(301) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_wrong_status_code(self, mock_get_request): mock_get_request.return_value = create_response( 201, {"location": "https://test.com"}) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_redirect_wrong_redirect_code(self, mock_get_request): mock_get_request.return_value = create_response( 303, {"location": "https://test.com"}) self.assertFalse(self.validator.redirect("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_empty(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": ""}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_malformed(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "sdfal"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_bad_max_age(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=not-an-int"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_expire(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=3600"}) self.assertFalse(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": "max-age=31536000"}) self.assertTrue(self.validator.hsts("test.com")) - @mock.patch("letsencrypt_compatibility_test.validator.requests.get") + @mock.patch("certbot_compatibility_test.validator.requests.get") def test_hsts_include_subdomains(self, mock_get_request): mock_get_request.return_value = create_response( headers={"strict-transport-security": diff --git a/letsencrypt-compatibility-test/docs/.gitignore b/certbot-compatibility-test/docs/.gitignore similarity index 100% rename from letsencrypt-compatibility-test/docs/.gitignore rename to certbot-compatibility-test/docs/.gitignore diff --git a/letsencrypt-compatibility-test/docs/Makefile b/certbot-compatibility-test/docs/Makefile similarity index 96% rename from letsencrypt-compatibility-test/docs/Makefile rename to certbot-compatibility-test/docs/Makefile index 90582a59b..0c9cf40aa 100644 --- a/letsencrypt-compatibility-test/docs/Makefile +++ b/certbot-compatibility-test/docs/Makefile @@ -87,9 +87,9 @@ 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 "# qcollectiongenerator $(BUILDDIR)/qthelp/certbot-compatibility-test.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letsencrypt-compatibility-test.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/certbot-compatibility-test.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ 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 "# mkdir -p $$HOME/.local/share/devhelp/certbot-compatibility-test" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/certbot-compatibility-test" @echo "# devhelp" epub: diff --git a/letsencrypt-compatibility-test/docs/_static/.gitignore b/certbot-compatibility-test/docs/_static/.gitignore similarity index 100% rename from letsencrypt-compatibility-test/docs/_static/.gitignore rename to certbot-compatibility-test/docs/_static/.gitignore diff --git a/letsencrypt-compatibility-test/docs/_templates/.gitignore b/certbot-compatibility-test/docs/_templates/.gitignore similarity index 100% rename from letsencrypt-compatibility-test/docs/_templates/.gitignore rename to certbot-compatibility-test/docs/_templates/.gitignore diff --git a/letsencrypt-compatibility-test/docs/api.rst b/certbot-compatibility-test/docs/api.rst similarity index 100% rename from letsencrypt-compatibility-test/docs/api.rst rename to certbot-compatibility-test/docs/api.rst diff --git a/certbot-compatibility-test/docs/api/index.rst b/certbot-compatibility-test/docs/api/index.rst new file mode 100644 index 000000000..fea92d2e5 --- /dev/null +++ b/certbot-compatibility-test/docs/api/index.rst @@ -0,0 +1,53 @@ +:mod:`certbot_compatibility_test` +------------------------------------- + +.. automodule:: certbot_compatibility_test + :members: + +:mod:`certbot_compatibility_test.errors` +============================================ + +.. automodule:: certbot_compatibility_test.errors + :members: + +:mod:`certbot_compatibility_test.interfaces` +================================================ + +.. automodule:: certbot_compatibility_test.interfaces + :members: + +:mod:`certbot_compatibility_test.test_driver` +================================================= + +.. automodule:: certbot_compatibility_test.test_driver + :members: + +:mod:`certbot_compatibility_test.util` +========================================== + +.. automodule:: certbot_compatibility_test.util + :members: + +:mod:`certbot_compatibility_test.configurators` +=================================================== + +.. automodule:: certbot_compatibility_test.configurators + :members: + +:mod:`certbot_compatibility_test.configurators.apache` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. automodule:: certbot_compatibility_test.configurators.apache + :members: + +:mod:`certbot_compatibility_test.configurators.apache.apache24` +------------------------------------------------------------------- + +.. automodule:: certbot_compatibility_test.configurators.apache.apache24 + :members: + +:mod:`certbot_compatibility_test.configurators.apache.common` +------------------------------------------------------------------- + +.. automodule:: certbot_compatibility_test.configurators.apache.common + :members: diff --git a/letsencrypt-compatibility-test/docs/conf.py b/certbot-compatibility-test/docs/conf.py similarity index 93% rename from letsencrypt-compatibility-test/docs/conf.py rename to certbot-compatibility-test/docs/conf.py index 3ee161efb..6aea7121e 100644 --- a/letsencrypt-compatibility-test/docs/conf.py +++ b/certbot-compatibility-test/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letsencrypt-compatibility-test documentation build configuration file, created by +# certbot-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 @@ -59,7 +59,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letsencrypt-compatibility-test' +project = u'certbot-compatibility-test' copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' @@ -221,7 +221,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letsencrypt-compatibility-testdoc' +htmlhelp_basename = 'certbot-compatibility-testdoc' # -- Options for LaTeX output --------------------------------------------- @@ -243,8 +243,8 @@ latex_elements = { # (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', + (master_doc, 'certbot-compatibility-test.tex', + u'certbot-compatibility-test Documentation', u'Let\'s Encrypt Project', 'manual'), ] @@ -274,8 +274,8 @@ latex_documents = [ # 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', + (master_doc, 'certbot-compatibility-test', + u'certbot-compatibility-test Documentation', [author], 1) ] @@ -289,9 +289,9 @@ man_pages = [ # (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', + (master_doc, 'certbot-compatibility-test', + u'certbot-compatibility-test Documentation', + author, 'certbot-compatibility-test', 'One line description of project.', 'Miscellaneous'), ] @@ -311,9 +311,9 @@ texinfo_documents = [ 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': ( + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot-apache': ( 'https://letsencrypt-apache.readthedocs.org/en/latest/', None), - 'letsencrypt-nginx': ( + 'certbot-nginx': ( 'https://letsencrypt-nginx.readthedocs.org/en/latest/', None), } diff --git a/letsencrypt-compatibility-test/docs/index.rst b/certbot-compatibility-test/docs/index.rst similarity index 75% rename from letsencrypt-compatibility-test/docs/index.rst rename to certbot-compatibility-test/docs/index.rst index df57ee6e6..a5e71e844 100644 --- a/letsencrypt-compatibility-test/docs/index.rst +++ b/certbot-compatibility-test/docs/index.rst @@ -1,9 +1,9 @@ -.. letsencrypt-compatibility-test documentation master file, created by +.. certbot-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! +Welcome to certbot-compatibility-test's documentation! ========================================================== Contents: diff --git a/letsencrypt-compatibility-test/docs/make.bat b/certbot-compatibility-test/docs/make.bat similarity index 97% rename from letsencrypt-compatibility-test/docs/make.bat rename to certbot-compatibility-test/docs/make.bat index c75269bdc..b6c0360f4 100644 --- a/letsencrypt-compatibility-test/docs/make.bat +++ b/certbot-compatibility-test/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "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.^> qcollectiongenerator %BUILDDIR%\qthelp\certbot-compatibility-test.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letsencrypt-compatibility-test.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\certbot-compatibility-test.ghc goto end ) diff --git a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt b/certbot-compatibility-test/readthedocs.org.requirements.txt similarity index 88% rename from letsencrypt-compatibility-test/readthedocs.org.requirements.txt rename to certbot-compatibility-test/readthedocs.org.requirements.txt index 957a8a157..c2a0c1110 100644 --- a/letsencrypt-compatibility-test/readthedocs.org.requirements.txt +++ b/certbot-compatibility-test/readthedocs.org.requirements.txt @@ -9,5 +9,5 @@ -e acme -e . --e letsencrypt-apache --e letsencrypt-compatibility-test[docs] +-e certbot-apache +-e certbot-compatibility-test[docs] diff --git a/letsencrypt-compatibility-test/setup.py b/certbot-compatibility-test/setup.py similarity index 87% rename from letsencrypt-compatibility-test/setup.py rename to certbot-compatibility-test/setup.py index 25104aeef..a39cd7d35 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -7,8 +7,8 @@ from setuptools import find_packages version = '0.6.0.dev0' install_requires = [ - 'letsencrypt=={0}'.format(version), - 'letsencrypt-apache=={0}'.format(version), + 'certbot=={0}'.format(version), + 'certbot-apache=={0}'.format(version), 'docker-py', 'requests', 'zope.interface', @@ -31,7 +31,7 @@ docs_extras = [ ] setup( - name='letsencrypt-compatibility-test', + name='certbot-compatibility-test', version=version, description="Compatibility tests for Let's Encrypt client", url='https://github.com/letsencrypt/letsencrypt', @@ -58,7 +58,7 @@ setup( }, entry_points={ 'console_scripts': [ - 'letsencrypt-compatibility-test = letsencrypt_compatibility_test.test_driver:main', + 'certbot-compatibility-test = certbot_compatibility_test.test_driver:main', ], }, ) diff --git a/letsencrypt-compatibility-test/MANIFEST.in b/letsencrypt-compatibility-test/MANIFEST.in deleted file mode 100644 index 24d777841..000000000 --- a/letsencrypt-compatibility-test/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -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 -recursive-include letsencrypt_compatibility_test/testdata * diff --git a/letsencrypt-compatibility-test/docs/api/index.rst b/letsencrypt-compatibility-test/docs/api/index.rst deleted file mode 100644 index f792a2cc3..000000000 --- a/letsencrypt-compatibility-test/docs/api/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -:mod:`letsencrypt_compatibility_test` -------------------------------------- - -.. automodule:: letsencrypt_compatibility_test - :members: - -:mod:`letsencrypt_compatibility_test.errors` -============================================ - -.. automodule:: letsencrypt_compatibility_test.errors - :members: - -:mod:`letsencrypt_compatibility_test.interfaces` -================================================ - -.. automodule:: letsencrypt_compatibility_test.interfaces - :members: - -:mod:`letsencrypt_compatibility_test.test_driver` -================================================= - -.. automodule:: letsencrypt_compatibility_test.test_driver - :members: - -:mod:`letsencrypt_compatibility_test.util` -========================================== - -.. automodule:: letsencrypt_compatibility_test.util - :members: - -:mod:`letsencrypt_compatibility_test.configurators` -=================================================== - -.. automodule:: letsencrypt_compatibility_test.configurators - :members: - -:mod:`letsencrypt_compatibility_test.configurators.apache` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. automodule:: letsencrypt_compatibility_test.configurators.apache - :members: - -:mod:`letsencrypt_compatibility_test.configurators.apache.apache24` -------------------------------------------------------------------- - -.. automodule:: letsencrypt_compatibility_test.configurators.apache.apache24 - :members: - -:mod:`letsencrypt_compatibility_test.configurators.apache.common` -------------------------------------------------------------------- - -.. automodule:: letsencrypt_compatibility_test.configurators.apache.common - :members: diff --git a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile b/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile deleted file mode 100644 index 392f5efa6..000000000 --- a/letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM httpd -MAINTAINER Brad Warren - -RUN mkdir /var/run/apache2 - -ENV APACHE_RUN_USER=daemon \ - APACHE_RUN_GROUP=daemon \ - APACHE_PID_FILE=/usr/local/apache2/logs/httpd.pid \ - APACHE_RUN_DIR=/var/run/apache2 \ - APACHE_LOCK_DIR=/var/lock \ - APACHE_LOG_DIR=/usr/local/apache2/logs - -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2enmod.sh /usr/local/bin/ -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/configurators/apache/a2dismod.sh /usr/local/bin/ -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/rsa1024_key2.pem /usr/local/apache2/conf/ -COPY letsencrypt-compatibility-test/letsencrypt_compatibility_test/testdata/empty_cert.pem /usr/local/apache2/conf/ - -# Note: this only exposes the port to other docker containers. You -# still have to bind to 443@host at runtime. -EXPOSE 443 diff --git a/tox.cover.sh b/tox.cover.sh index 7097623be..b40f54c15 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -9,7 +9,7 @@ # -e makes sure we fail fast and don't submit coveralls submit if [ "xxx$1" = "xxx" ]; then - pkgs="certbot acme letsencrypt_apache letsencrypt_nginx letshelp_letsencrypt" + pkgs="certbot acme certbot_apache certbot_nginx letshelp_letsencrypt" else pkgs="$@" fi @@ -19,9 +19,9 @@ cover () { min=98 elif [ "$1" = "acme" ]; then min=100 - elif [ "$1" = "letsencrypt_apache" ]; then + elif [ "$1" = "certbot_apache" ]; then min=100 - elif [ "$1" = "letsencrypt_nginx" ]; then + elif [ "$1" = "certbot_nginx" ]; then min=97 elif [ "$1" = "letshelp_letsencrypt" ]; then min=100 diff --git a/tox.ini b/tox.ini index cbed0fac7..b0472c56b 100644 --- a/tox.ini +++ b/tox.ini @@ -64,19 +64,19 @@ basepython = python2.7 # duplicate code checking; if one of the commands fails, others will # continue, but tox return code will reflect previous error commands = - pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-letsencrypt ./pep8.travis.sh pylint --rcfile=.pylintrc certbot pylint --rcfile=acme/.pylintrc acme/acme pylint --rcfile=.pylintrc certbot-apache/certbot_apache pylint --rcfile=.pylintrc certbot-nginx/certbot_nginx - pylint --rcfile=.pylintrc letsencrypt-compatibility-test/letsencrypt_compatibility_test + pylint --rcfile=.pylintrc certbot-compatibility-test/certbot_compatibility_test pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt [testenv:apacheconftest] #basepython = python2.7 commands = - pip install -e acme -e .[dev] -e certbot-apache -e certbot-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt + pip install -e acme -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-letsencrypt {toxinidir}/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test --debian-modules From 0ce45a77f940013b1fc3f4e83b0e29102a98e69f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 16:59:37 -0700 Subject: [PATCH 217/317] s/Let's Encrypt/Certbot certbot-compatibility-test --- .../certbot_compatibility_test/__init__.py | 2 +- .../configurators/__init__.py | 2 +- .../configurators/apache/__init__.py | 2 +- .../certbot_compatibility_test/errors.py | 4 ++-- .../certbot_compatibility_test/interfaces.py | 10 +++++----- .../certbot_compatibility_test/test_driver.py | 4 ++-- certbot-compatibility-test/setup.py | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/certbot-compatibility-test/certbot_compatibility_test/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/__init__.py index 90807863a..5ee547703 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/__init__.py +++ b/certbot-compatibility-test/certbot_compatibility_test/__init__.py @@ -1 +1 @@ -"""Let's Encrypt compatibility test""" +"""Certbot compatibility test""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py index bf7b3471f..e553ff438 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/__init__.py @@ -1 +1 @@ -"""Let's Encrypt compatibility test configurators""" +"""Certbot compatibility test configurators""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py index 9feca23d4..d559d0645 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py +++ b/certbot-compatibility-test/certbot_compatibility_test/configurators/apache/__init__.py @@ -1 +1 @@ -"""Let's Encrypt compatibility test Apache configurators""" +"""Certbot compatibility test Apache configurators""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/errors.py b/certbot-compatibility-test/certbot_compatibility_test/errors.py index 3b7eb6911..e6a235e70 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/errors.py +++ b/certbot-compatibility-test/certbot_compatibility_test/errors.py @@ -1,5 +1,5 @@ -"""Let's Encrypt compatibility test errors""" +"""Certbot compatibility test errors""" class Error(Exception): - """Generic Let's Encrypt compatibility test error""" + """Generic Certbot compatibility test error""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/interfaces.py b/certbot-compatibility-test/certbot_compatibility_test/interfaces.py index 8f0a6b4c5..cd367d9af 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/interfaces.py +++ b/certbot-compatibility-test/certbot_compatibility_test/interfaces.py @@ -1,4 +1,4 @@ -"""Let's Encrypt compatibility test interfaces""" +"""Certbot compatibility test interfaces""" import zope.interface import certbot.interfaces @@ -7,7 +7,7 @@ import certbot.interfaces class IPluginProxy(zope.interface.Interface): - """Wraps a Let's Encrypt plugin""" + """Wraps a Certbot plugin""" http_port = zope.interface.Attribute( "The port to connect to on localhost for HTTP traffic") @@ -38,15 +38,15 @@ class IPluginProxy(zope.interface.Interface): class IAuthenticatorProxy(IPluginProxy, certbot.interfaces.IAuthenticator): - """Wraps a Let's Encrypt authenticator""" + """Wraps a Certbot authenticator""" class IInstallerProxy(IPluginProxy, certbot.interfaces.IInstaller): - """Wraps a Let's Encrypt installer""" + """Wraps a Certbot installer""" def get_all_names_answer(): """Returns all names that should be found by the installer""" class IConfiguratorProxy(IAuthenticatorProxy, IInstallerProxy): - """Wraps a Let's Encrypt configurator""" + """Wraps a Certbot configurator""" diff --git a/certbot-compatibility-test/certbot_compatibility_test/test_driver.py b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py index 6d9ebdada..6823dfdab 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/test_driver.py +++ b/certbot-compatibility-test/certbot_compatibility_test/test_driver.py @@ -1,4 +1,4 @@ -"""Tests Let's Encrypt plugins against different server configurations.""" +"""Tests Certbot plugins against different server configurations.""" import argparse import filecmp import functools @@ -25,7 +25,7 @@ from certbot_compatibility_test.configurators.apache import apache24 DESCRIPTION = """ -Tests Let's Encrypt plugins against different server configuratons. It is +Tests Certbot plugins against different server configuratons. It is assumed that Docker is already installed. If no test types is specified, all tests that the plugin supports are performed. diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index a39cd7d35..51704fda5 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -33,9 +33,9 @@ docs_extras = [ setup( name='certbot-compatibility-test', version=version, - description="Compatibility tests for Let's Encrypt client", + description="Compatibility tests for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Electronic Frontier Foundation", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ From 214343ed6aa9db12524a45df34f5a26b73abed67 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 13 Apr 2016 17:42:19 -0700 Subject: [PATCH 218/317] rename letshelp-letsencrypt --- .../LICENSE.txt | 0 .../MANIFEST.in | 2 +- letshelp-certbot/README.rst | 1 + .../docs/.gitignore | 0 .../docs/Makefile | 8 ++++---- .../docs/_static/.gitignore | 0 .../docs/_templates/.gitignore | 0 .../docs/api.rst | 0 letshelp-certbot/docs/api/index.rst | 11 +++++++++++ .../docs/conf.py | 16 ++++++++-------- .../docs/index.rst | 4 ++-- .../docs/make.bat | 4 ++-- .../letshelp_certbot}/__init__.py | 0 .../letshelp_certbot}/apache.py | 13 +++++++------ .../letshelp_certbot}/apache_test.py | 6 +++--- .../testdata/mods-available/ssl.load | 0 .../testdata/mods-enabled/ssl.load | 0 .../testdata/super_secret_file.txt | 0 .../testdata/uncommonly_named_k3y | 0 .../testdata/uncommonly_named_p4sswd | 0 .../readthedocs.org.requirements.txt | 2 +- .../setup.py | 10 +++++----- letshelp-letsencrypt/README.rst | 1 - letshelp-letsencrypt/docs/api/index.rst | 11 ----------- tools/_venv_common.sh | 4 ++-- tools/deps.sh | 4 ++-- tools/venv.sh | 8 ++++---- tox.cover.sh | 4 ++-- tox.ini | 12 ++++++------ 29 files changed, 61 insertions(+), 60 deletions(-) rename {letshelp-letsencrypt => letshelp-certbot}/LICENSE.txt (100%) rename {letshelp-letsencrypt => letshelp-certbot}/MANIFEST.in (56%) create mode 100644 letshelp-certbot/README.rst rename {letshelp-letsencrypt => letshelp-certbot}/docs/.gitignore (100%) rename {letshelp-letsencrypt => letshelp-certbot}/docs/Makefile (97%) rename {letshelp-letsencrypt => letshelp-certbot}/docs/_static/.gitignore (100%) rename {letshelp-letsencrypt => letshelp-certbot}/docs/_templates/.gitignore (100%) rename {letshelp-letsencrypt => letshelp-certbot}/docs/api.rst (100%) create mode 100644 letshelp-certbot/docs/api/index.rst rename {letshelp-letsencrypt => letshelp-certbot}/docs/conf.py (94%) rename {letshelp-letsencrypt => letshelp-certbot}/docs/index.rst (77%) rename {letshelp-letsencrypt => letshelp-certbot}/docs/make.bat (97%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/__init__.py (100%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/apache.py (96%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/apache_test.py (98%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/testdata/mods-available/ssl.load (100%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/testdata/mods-enabled/ssl.load (100%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/testdata/super_secret_file.txt (100%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/testdata/uncommonly_named_k3y (100%) rename {letshelp-letsencrypt/letshelp_letsencrypt => letshelp-certbot/letshelp_certbot}/testdata/uncommonly_named_p4sswd (100%) rename {letshelp-letsencrypt => letshelp-certbot}/readthedocs.org.requirements.txt (94%) rename {letshelp-letsencrypt => letshelp-certbot}/setup.py (85%) delete mode 100644 letshelp-letsencrypt/README.rst delete mode 100644 letshelp-letsencrypt/docs/api/index.rst diff --git a/letshelp-letsencrypt/LICENSE.txt b/letshelp-certbot/LICENSE.txt similarity index 100% rename from letshelp-letsencrypt/LICENSE.txt rename to letshelp-certbot/LICENSE.txt diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-certbot/MANIFEST.in similarity index 56% rename from letshelp-letsencrypt/MANIFEST.in rename to letshelp-certbot/MANIFEST.in index 6ea55a950..623392f28 100644 --- a/letshelp-letsencrypt/MANIFEST.in +++ b/letshelp-certbot/MANIFEST.in @@ -1,4 +1,4 @@ include LICENSE.txt include README.rst recursive-include docs * -recursive-include letshelp_letsencrypt/testdata * +recursive-include letshelp_certbot/testdata * diff --git a/letshelp-certbot/README.rst b/letshelp-certbot/README.rst new file mode 100644 index 000000000..bbe2f2570 --- /dev/null +++ b/letshelp-certbot/README.rst @@ -0,0 +1 @@ +Let's help Certbot client diff --git a/letshelp-letsencrypt/docs/.gitignore b/letshelp-certbot/docs/.gitignore similarity index 100% rename from letshelp-letsencrypt/docs/.gitignore rename to letshelp-certbot/docs/.gitignore diff --git a/letshelp-letsencrypt/docs/Makefile b/letshelp-certbot/docs/Makefile similarity index 97% rename from letshelp-letsencrypt/docs/Makefile rename to letshelp-certbot/docs/Makefile index 8e742d837..4b392ab8d 100644 --- a/letshelp-letsencrypt/docs/Makefile +++ b/letshelp-certbot/docs/Makefile @@ -87,9 +87,9 @@ 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 "# qcollectiongenerator $(BUILDDIR)/qthelp/letshelp-certbot.qhcp" @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letshelp-letsencrypt.qhc" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/letshelp-certbot.qhc" applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @@ -104,8 +104,8 @@ 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 "# mkdir -p $$HOME/.local/share/devhelp/letshelp-certbot" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/letshelp-certbot" @echo "# devhelp" epub: diff --git a/letshelp-letsencrypt/docs/_static/.gitignore b/letshelp-certbot/docs/_static/.gitignore similarity index 100% rename from letshelp-letsencrypt/docs/_static/.gitignore rename to letshelp-certbot/docs/_static/.gitignore diff --git a/letshelp-letsencrypt/docs/_templates/.gitignore b/letshelp-certbot/docs/_templates/.gitignore similarity index 100% rename from letshelp-letsencrypt/docs/_templates/.gitignore rename to letshelp-certbot/docs/_templates/.gitignore diff --git a/letshelp-letsencrypt/docs/api.rst b/letshelp-certbot/docs/api.rst similarity index 100% rename from letshelp-letsencrypt/docs/api.rst rename to letshelp-certbot/docs/api.rst diff --git a/letshelp-certbot/docs/api/index.rst b/letshelp-certbot/docs/api/index.rst new file mode 100644 index 000000000..5ced5f501 --- /dev/null +++ b/letshelp-certbot/docs/api/index.rst @@ -0,0 +1,11 @@ +:mod:`letshelp_certbot` +--------------------------- + +.. automodule:: letshelp_certbot + :members: + +:mod:`letshelp_certbot.apache` +================================== + +.. automodule:: letshelp_certbot.apache + :members: diff --git a/letshelp-letsencrypt/docs/conf.py b/letshelp-certbot/docs/conf.py similarity index 94% rename from letshelp-letsencrypt/docs/conf.py rename to letshelp-certbot/docs/conf.py index a84c4c982..ba669113a 100644 --- a/letshelp-letsencrypt/docs/conf.py +++ b/letshelp-certbot/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# letshelp-letsencrypt documentation build configuration file, created by +# letshelp-certbot 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 @@ -58,7 +58,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'letshelp-letsencrypt' +project = u'letshelp-certbot' copyright = u'2014-2015, Let\'s Encrypt Project' author = u'Let\'s Encrypt Project' @@ -220,7 +220,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'letshelp-letsencryptdoc' +htmlhelp_basename = 'letshelp-certbotdoc' # -- Options for LaTeX output --------------------------------------------- @@ -242,7 +242,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'letshelp-letsencrypt.tex', u'letshelp-letsencrypt Documentation', + (master_doc, 'letshelp-certbot.tex', u'letshelp-certbot Documentation', u'Let\'s Encrypt Project', 'manual'), ] @@ -272,7 +272,7 @@ latex_documents = [ # 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', + (master_doc, 'letshelp-certbot', u'letshelp-certbot Documentation', [author], 1) ] @@ -286,8 +286,8 @@ man_pages = [ # (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.', + (master_doc, 'letshelp-certbot', u'letshelp-certbot Documentation', + author, 'letshelp-certbot', 'One line description of project.', 'Miscellaneous'), ] @@ -307,5 +307,5 @@ texinfo_documents = [ 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), + 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), } diff --git a/letshelp-letsencrypt/docs/index.rst b/letshelp-certbot/docs/index.rst similarity index 77% rename from letshelp-letsencrypt/docs/index.rst rename to letshelp-certbot/docs/index.rst index 6b67a2e1f..678d9be2e 100644 --- a/letshelp-letsencrypt/docs/index.rst +++ b/letshelp-certbot/docs/index.rst @@ -1,9 +1,9 @@ -.. letshelp-letsencrypt documentation master file, created by +.. letshelp-certbot 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! +Welcome to letshelp-certbot's documentation! ================================================ Contents: diff --git a/letshelp-letsencrypt/docs/make.bat b/letshelp-certbot/docs/make.bat similarity index 97% rename from letshelp-letsencrypt/docs/make.bat rename to letshelp-certbot/docs/make.bat index 006f7825d..0229b4f69 100644 --- a/letshelp-letsencrypt/docs/make.bat +++ b/letshelp-certbot/docs/make.bat @@ -127,9 +127,9 @@ if "%1" == "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.^> qcollectiongenerator %BUILDDIR%\qthelp\letshelp-certbot.qhcp echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letshelp-letsencrypt.ghc + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\letshelp-certbot.ghc goto end ) diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py b/letshelp-certbot/letshelp_certbot/__init__.py similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/__init__.py rename to letshelp-certbot/letshelp_certbot/__init__.py diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/apache.py b/letshelp-certbot/letshelp_certbot/apache.py similarity index 96% rename from letshelp-letsencrypt/letshelp_letsencrypt/apache.py rename to letshelp-certbot/letshelp_certbot/apache.py index d7cb05b70..5752bdab0 100755 --- a/letshelp-letsencrypt/letshelp_letsencrypt/apache.py +++ b/letshelp-certbot/letshelp_certbot/apache.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -"""Let's Encrypt Apache configuration submission script""" +"""Certbot Apache configuration submission script""" from __future__ import print_function @@ -17,12 +17,12 @@ import textwrap _DESCRIPTION = """ -Let's Help is a simple script you can run to help out the Let's Encrypt -project. Since Let's Encrypt will support automatically configuring HTTPS on +Let's Help is a simple script you can run to help out the Certbot +project. Since Certbot will support automatically configuring HTTPS on many servers, we want to test this functionality on as many configurations as possible. This script will create a sanitized copy of your Apache configuration, notifying you of the files that have been selected. If (and only -if) you approve this selection, these files will be sent to the Let's Encrypt +if) you approve this selection, these files will be sent to the Certbot developers. """ @@ -38,8 +38,9 @@ argument and the path to the binary. # Keywords likely to be found in filenames of sensitive files _SENSITIVE_FILENAME_REGEX = re.compile(r"^(?!.*proxy_fdpass).*pass.*$|private|" - r"secret|cert|crt|key|rsa|dsa|pw|\.pem|" - r"\.der|\.p12|\.pfx|\.p7b") + r"secret|^(?!.*certbot).*cert.*$|crt|" + r"key|rsa|dsa|pw|\.pem|\.der|\.p12|" + r"\.pfx|\.p7b") def make_and_verify_selection(server_root, temp_dir): diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/apache_test.py b/letshelp-certbot/letshelp_certbot/apache_test.py similarity index 98% rename from letshelp-letsencrypt/letshelp_letsencrypt/apache_test.py rename to letshelp-certbot/letshelp_certbot/apache_test.py index 7ed1df760..0c1b5f2f6 100644 --- a/letshelp-letsencrypt/letshelp_letsencrypt/apache_test.py +++ b/letshelp-certbot/letshelp_certbot/apache_test.py @@ -1,4 +1,4 @@ -"""Tests for letshelp.letshelp_letsencrypt_apache.py""" +"""Tests for letshelp.letshelp_certbot_apache.py""" import argparse import functools import os @@ -10,7 +10,7 @@ import unittest import mock -import letshelp_letsencrypt.apache as letshelp_le_apache +import letshelp_certbot.apache as letshelp_le_apache _PARTIAL_CONF_PATH = os.path.join("mods-available", "ssl.load") @@ -25,7 +25,7 @@ _SECRET_FILE = pkg_resources.resource_filename( __name__, os.path.join("testdata", "super_secret_file.txt")) -_MODULE_NAME = "letshelp_letsencrypt.apache" +_MODULE_NAME = "letshelp_certbot.apache" _COMPILE_SETTINGS = """Server version: Apache/2.4.10 (Debian) diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-available/ssl.load b/letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-available/ssl.load rename to letshelp-certbot/letshelp_certbot/testdata/mods-available/ssl.load diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-enabled/ssl.load b/letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/mods-enabled/ssl.load rename to letshelp-certbot/letshelp_certbot/testdata/mods-enabled/ssl.load diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/super_secret_file.txt b/letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/super_secret_file.txt rename to letshelp-certbot/letshelp_certbot/testdata/super_secret_file.txt diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_k3y b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_k3y rename to letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_k3y diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_p4sswd b/letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd similarity index 100% rename from letshelp-letsencrypt/letshelp_letsencrypt/testdata/uncommonly_named_p4sswd rename to letshelp-certbot/letshelp_certbot/testdata/uncommonly_named_p4sswd diff --git a/letshelp-letsencrypt/readthedocs.org.requirements.txt b/letshelp-certbot/readthedocs.org.requirements.txt similarity index 94% rename from letshelp-letsencrypt/readthedocs.org.requirements.txt rename to letshelp-certbot/readthedocs.org.requirements.txt index 898d2716e..7858b312f 100644 --- a/letshelp-letsencrypt/readthedocs.org.requirements.txt +++ b/letshelp-certbot/readthedocs.org.requirements.txt @@ -7,4 +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 letshelp-letsencrypt[docs] +-e letshelp-certbot[docs] diff --git a/letshelp-letsencrypt/setup.py b/letshelp-certbot/setup.py similarity index 85% rename from letshelp-letsencrypt/setup.py rename to letshelp-certbot/setup.py index d505858e4..e625b288c 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-certbot/setup.py @@ -20,11 +20,11 @@ docs_extras = [ ] setup( - name='letshelp-letsencrypt', + name='letshelp-certbot', version=version, - description="Let's help Let's Encrypt client", + description="Let's help Certbot client", url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Electronic Frontier Foundation", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ @@ -52,8 +52,8 @@ setup( }, entry_points={ 'console_scripts': [ - 'letshelp-letsencrypt-apache = letshelp_letsencrypt.apache:main', + 'letshelp-certbot-apache = letshelp_certbot.apache:main', ], }, - test_suite='letshelp_letsencrypt', + test_suite='letshelp_certbot', ) diff --git a/letshelp-letsencrypt/README.rst b/letshelp-letsencrypt/README.rst deleted file mode 100644 index 159048d6d..000000000 --- a/letshelp-letsencrypt/README.rst +++ /dev/null @@ -1 +0,0 @@ -Let's help Let's Encrypt client diff --git a/letshelp-letsencrypt/docs/api/index.rst b/letshelp-letsencrypt/docs/api/index.rst deleted file mode 100644 index 8f6872eac..000000000 --- a/letshelp-letsencrypt/docs/api/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -:mod:`letshelp_letsencrypt` ---------------------------- - -.. automodule:: letshelp_letsencrypt - :members: - -:mod:`letshelp_letsencrypt.apache` -================================== - -.. automodule:: letshelp_letsencrypt.apache - :members: diff --git a/tools/_venv_common.sh b/tools/_venv_common.sh index d07f38ed8..a121af82d 100755 --- a/tools/_venv_common.sh +++ b/tools/_venv_common.sh @@ -3,8 +3,8 @@ VENV_NAME=${VENV_NAME:-venv} # .egg-info directories tend to cause bizzaire problems (e.g. `pip -e -# .` might unexpectedly install letshelp-letsencrypt only, in case -# `python letshelp-letsencrypt/setup.py build` has been called +# .` might unexpectedly install letshelp-certbot only, in case +# `python letshelp-certbot/setup.py build` has been called # earlier) rm -rf *.egg-info diff --git a/tools/deps.sh b/tools/deps.sh index 6fb2bf63b..e12f201a5 100755 --- a/tools/deps.sh +++ b/tools/deps.sh @@ -2,9 +2,9 @@ # # Find all Python imports. # -# ./tools/deps.sh letsencrypt +# ./tools/deps.sh certbot # ./tools/deps.sh acme -# ./tools/deps.sh letsencrypt-apache +# ./tools/deps.sh certbot-apache # ... # # Manually compare the output with deps in setup.py. diff --git a/tools/venv.sh b/tools/venv.sh index 73c3bb110..82712d33e 100755 --- a/tools/venv.sh +++ b/tools/venv.sh @@ -6,7 +6,7 @@ export VENV_ARGS="--python python2" ./tools/_venv_common.sh \ -e acme[dev] \ -e .[dev,docs] \ - -e letsencrypt-apache \ - -e letsencrypt-nginx \ - -e letshelp-letsencrypt \ - -e letsencrypt-compatibility-test + -e certbot-apache \ + -e certbot-nginx \ + -e letshelp-certbot \ + -e certbot-compatibility-test diff --git a/tox.cover.sh b/tox.cover.sh index b40f54c15..7243c4708 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -9,7 +9,7 @@ # -e makes sure we fail fast and don't submit coveralls submit if [ "xxx$1" = "xxx" ]; then - pkgs="certbot acme certbot_apache certbot_nginx letshelp_letsencrypt" + pkgs="certbot acme certbot_apache certbot_nginx letshelp_certbot" else pkgs="$@" fi @@ -23,7 +23,7 @@ cover () { min=100 elif [ "$1" = "certbot_nginx" ]; then min=97 - elif [ "$1" = "letshelp_letsencrypt" ]; then + elif [ "$1" = "letshelp_certbot" ]; then min=100 else echo "Unrecognized package: $1" diff --git a/tox.ini b/tox.ini index b0472c56b..5c88dfd21 100644 --- a/tox.ini +++ b/tox.ini @@ -21,8 +21,8 @@ commands = nosetests -v certbot_apache pip install -e certbot-nginx nosetests -v certbot_nginx - pip install -e letshelp-letsencrypt - nosetests -v letshelp_letsencrypt + pip install -e letshelp-certbot + nosetests -v letshelp_certbot setenv = PYTHONPATH = {toxinidir} @@ -54,7 +54,7 @@ commands = [testenv:cover] basepython = python2.7 commands = - pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e letshelp-certbot ./tox.cover.sh [testenv:lint] @@ -64,19 +64,19 @@ basepython = python2.7 # duplicate code checking; if one of the commands fails, others will # continue, but tox return code will reflect previous error commands = - pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-letsencrypt + pip install -e acme[dev] -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-certbot ./pep8.travis.sh pylint --rcfile=.pylintrc certbot pylint --rcfile=acme/.pylintrc acme/acme pylint --rcfile=.pylintrc certbot-apache/certbot_apache pylint --rcfile=.pylintrc certbot-nginx/certbot_nginx pylint --rcfile=.pylintrc certbot-compatibility-test/certbot_compatibility_test - pylint --rcfile=.pylintrc letshelp-letsencrypt/letshelp_letsencrypt + pylint --rcfile=.pylintrc letshelp-certbot/letshelp_certbot [testenv:apacheconftest] #basepython = python2.7 commands = - pip install -e acme -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-letsencrypt + pip install -e acme -e .[dev] -e certbot-apache -e certbot-nginx -e certbot-compatibility-test -e letshelp-certbot {toxinidir}/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test --debian-modules From 7228c0c9ed127782392bd8326f4dbec6e5714fb4 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 08:57:34 -0700 Subject: [PATCH 219/317] 'co' is not a git command --- tests/letstest/multitester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 876b7807f..ed7b7b5cf 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -257,7 +257,7 @@ def local_git_PR(repo_url, PRnumstr, merge_master=True): local('if [ -d letsencrypt ]; then rm -rf letsencrypt; fi') local('git clone %s letsencrypt'% repo_url) local('cd letsencrypt && git fetch origin pull/%s/head:lePRtest'%PRnumstr) - local('cd letsencrypt && git co lePRtest') + local('cd letsencrypt && git checkout lePRtest') if merge_master: local('cd letsencrypt && git remote update origin') local('cd letsencrypt && git merge origin/master -m "testmerge"') From ae6f1c62f11435061c2b30457961561f39afa0c7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 10:20:23 -0700 Subject: [PATCH 220/317] Rename misc files --- .gitignore | 1 + .travis.yml | 4 +- Dockerfile | 36 +++++++-------- Dockerfile-dev | 44 +++++++++---------- MANIFEST.in | 2 +- acme/setup.py | 2 +- certbot-compatibility-test/README.rst | 2 +- certbot/cli.py | 2 +- docker-compose.yml | 6 +-- examples/cli.ini | 2 +- examples/generate-csr.sh | 2 +- ..._plugins.py => certbot_example_plugins.py} | 8 ++-- examples/plugins/setup.py | 12 ++--- linter_plugin.py | 2 +- pep8.travis.sh | 8 ++-- tests/boulder-integration.sh | 4 +- tools/release.sh | 22 +++++----- tools/venv.sh | 2 +- tools/venv3.sh | 2 +- 19 files changed, 82 insertions(+), 81 deletions(-) rename examples/plugins/{letsencrypt_example_plugins.py => certbot_example_plugins.py} (82%) diff --git a/.gitignore b/.gitignore index 8118edfd4..b653cb06c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ dist*/ /.tox/ /releases/ letsencrypt.log +certbot.log letsencrypt-auto-source/letsencrypt-auto.sig.lzma.base64 # coverage diff --git a/.travis.yml b/.travis.yml index 38c874279..5d70ca799 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,7 +82,7 @@ sudo: false addons: # Custom /etc/hosts required for simple verification of http-01 - # and tls-sni-01, and for letsencrypt_test_nginx + # and tls-sni-01, and for certbot_test_nginx hosts: - le.wtf - le1.wtf @@ -105,7 +105,7 @@ addons: - libssl-dev - libffi-dev - ca-certificates - # For letsencrypt-nginx integration testing + # For certbot-nginx integration testing - nginx-light - openssl # For Boulder integration testing diff --git a/Dockerfile b/Dockerfile index ccbb07b95..b996558b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ EXPOSE 443 # authenticator and text mode only?) VOLUME /etc/letsencrypt /var/lib/letsencrypt -WORKDIR /opt/letsencrypt +WORKDIR /opt/certbot # no need to mkdir anything: # https://docs.docker.com/reference/builder/#copy @@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt # directories in its path. -COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto -RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ +COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto +RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* \ /tmp/* \ @@ -33,7 +33,7 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages- # Dockerfile we make sure we cache as much as possible -COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/certbot/src/ # all above files are necessary for setup.py and venv setup, however, # package source code directory has to be copied separately to a @@ -44,26 +44,26 @@ COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/ # copied, just its contents." Order again matters, three files are far # more likely to be cached than the whole project directory -COPY letsencrypt /opt/letsencrypt/src/letsencrypt/ -COPY acme /opt/letsencrypt/src/acme/ -COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ -COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ +COPY certbot /opt/certbot/src/certbot/ +COPY acme /opt/certbot/src/acme/ +COPY certbot-apache /opt/certbot/src/certbot-apache/ +COPY certbot-nginx /opt/certbot/src/certbot-nginx/ -RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv +RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv # PATH is set now so pipstrap upgrades the correct (v)env -ENV PATH /opt/letsencrypt/venv/bin:$PATH -RUN /opt/letsencrypt/venv/bin/python /opt/letsencrypt/src/pipstrap.py && \ - /opt/letsencrypt/venv/bin/pip install \ - -e /opt/letsencrypt/src/acme \ - -e /opt/letsencrypt/src \ - -e /opt/letsencrypt/src/letsencrypt-apache \ - -e /opt/letsencrypt/src/letsencrypt-nginx +ENV PATH /opt/certbot/venv/bin:$PATH +RUN /opt/certbot/venv/bin/python /opt/certbot/src/pipstrap.py && \ + /opt/certbot/venv/bin/pip install \ + -e /opt/certbot/src/acme \ + -e /opt/certbot/src \ + -e /opt/certbot/src/certbot-apache \ + -e /opt/certbot/src/certbot-nginx # install in editable mode (-e) to save space: it's not possible to -# "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image); +# "rm -rf /opt/certbot/src" (it's stays in the underlaying image); # this might also help in debugging: you can "docker run --entrypoint # bash" and investigate, apply patches, etc. -ENTRYPOINT [ "letsencrypt" ] +ENTRYPOINT [ "certbot" ] diff --git a/Dockerfile-dev b/Dockerfile-dev index 56e2ec05b..bfae9b087 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -13,7 +13,7 @@ EXPOSE 443 # authenticator and text mode only?) VOLUME /etc/letsencrypt /var/lib/letsencrypt -WORKDIR /opt/letsencrypt +WORKDIR /opt/certbot # no need to mkdir anything: # https://docs.docker.com/reference/builder/#copy @@ -22,8 +22,8 @@ WORKDIR /opt/letsencrypt # TODO: Install non-default Python versions for tox. # TODO: Install Apache/Nginx for plugin development. -COPY letsencrypt-auto-source/letsencrypt-auto /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto -RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ +COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto +RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* \ /tmp/* \ @@ -32,7 +32,7 @@ RUN /opt/letsencrypt/src/letsencrypt-auto-source/letsencrypt-auto --os-packages- # the above is not likely to change, so by putting it further up the # Dockerfile we make sure we cache as much as possible -COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/certbot/src/ # all above files are necessary for setup.py, however, package source # code directory has to be copied separately to a subdirectory... @@ -42,27 +42,27 @@ COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh t # copied, just its contents." Order again matters, three files are far # more likely to be cached than the whole project directory -COPY letsencrypt /opt/letsencrypt/src/letsencrypt/ -COPY acme /opt/letsencrypt/src/acme/ -COPY letsencrypt-apache /opt/letsencrypt/src/letsencrypt-apache/ -COPY letsencrypt-nginx /opt/letsencrypt/src/letsencrypt-nginx/ -COPY letshelp-letsencrypt /opt/letsencrypt/src/letshelp-letsencrypt/ -COPY letsencrypt-compatibility-test /opt/letsencrypt/src/letsencrypt-compatibility-test/ -COPY tests /opt/letsencrypt/src/tests/ +COPY certbot /opt/certbot/src/certbot/ +COPY acme /opt/certbot/src/acme/ +COPY certbot-apache /opt/certbot/src/certbot-apache/ +COPY certbot-nginx /opt/certbot/src/certbot-nginx/ +COPY letshelp-certbot /opt/certbot/src/letshelp-certbot/ +COPY certbot-compatibility-test /opt/certbot/src/certbot-compatibility-test/ +COPY tests /opt/certbot/src/tests/ -RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \ - /opt/letsencrypt/venv/bin/pip install \ - -e /opt/letsencrypt/src/acme \ - -e /opt/letsencrypt/src \ - -e /opt/letsencrypt/src/letsencrypt-apache \ - -e /opt/letsencrypt/src/letsencrypt-nginx \ - -e /opt/letsencrypt/src/letshelp-letsencrypt \ - -e /opt/letsencrypt/src/letsencrypt-compatibility-test \ - -e /opt/letsencrypt/src[dev,docs] +RUN virtualenv --no-site-packages -p python2 /opt/certbot/venv && \ + /opt/certbot/venv/bin/pip install \ + -e /opt/certbot/src/acme \ + -e /opt/certbot/src \ + -e /opt/certbot/src/certbot-apache \ + -e /opt/certbot/src/certbot-nginx \ + -e /opt/certbot/src/letshelp-certbot \ + -e /opt/certbot/src/certbot-compatibility-test \ + -e /opt/certbot/src[dev,docs] # install in editable mode (-e) to save space: it's not possible to -# "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image); +# "rm -rf /opt/certbot/src" (it's stays in the underlaying image); # this might also help in debugging: you can "docker run --entrypoint # bash" and investigate, apply patches, etc. -ENV PATH /opt/letsencrypt/venv/bin:$PATH +ENV PATH /opt/certbot/venv/bin:$PATH diff --git a/MANIFEST.in b/MANIFEST.in index a6f9ae2b6..18393e3e1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,4 +5,4 @@ include LICENSE.txt include linter_plugin.py recursive-include docs * recursive-include examples * -recursive-include letsencrypt/tests/testdata * +recursive-include certbot/tests/testdata * diff --git a/acme/setup.py b/acme/setup.py index 41c04fd33..72c751dca 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -53,7 +53,7 @@ setup( version=version, description='ACME protocol implementation in Python', url='https://github.com/letsencrypt/letsencrypt', - author="Let's Encrypt Project", + author="Electronic Frontier Foundation", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/certbot-compatibility-test/README.rst b/certbot-compatibility-test/README.rst index 4afd999a8..9333b5680 100644 --- a/certbot-compatibility-test/README.rst +++ b/certbot-compatibility-test/README.rst @@ -1 +1 @@ -Compatibility tests for Let's Encrypt client +Compatibility tests for Certbot diff --git a/certbot/cli.py b/certbot/cli.py index e920ab358..e2c57595b 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -634,7 +634,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "at this system. This option cannot be used with --csr.") helpful.add( "automation", "--agree-tos", dest="tos", action="store_true", - help="Agree to the Let's Encrypt Subscriber Agreement") + help="Agree to the ACME Subscriber Agreement") helpful.add( "automation", "--account", metavar="ACCOUNT_ID", help="Account ID to use") diff --git a/docker-compose.yml b/docker-compose.yml index dbe6e4f01..8b2a8e9a3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,12 +3,12 @@ production: ports: - "443:443" -# For development, mount git root to /opt/letsencrypt/src in order to +# For development, mount git root to /opt/certbot/src in order to # make the dev workflow more vagrant-like. development: build: . ports: - "443:443" volumes: - - .:/opt/letsencrypt/src - - /opt/letsencrypt/venv + - .:/opt/certbot/src + - /opt/certbot/venv diff --git a/examples/cli.ini b/examples/cli.ini index f0c993c57..63af3cc49 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -1,5 +1,5 @@ # 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 +# All flags used by the client can be configured here. Run Certbot with # "--help" to learn more about the available options. # Use a 4096 bit RSA key instead of 2048 diff --git a/examples/generate-csr.sh b/examples/generate-csr.sh index c4a3af016..55f6c7b9f 100755 --- a/examples/generate-csr.sh +++ b/examples/generate-csr.sh @@ -25,4 +25,4 @@ SAN="$domains" openssl req -config "${OPENSSL_CNF:-openssl.cnf}" \ -outform DER # 512 or 1024 too low for Boulder, 2048 is smallest for tests -echo "You can now run: letsencrypt auth --csr ${CSR_PATH:-csr.der}" +echo "You can now run: certbot auth --csr ${CSR_PATH:-csr.der}" diff --git a/examples/plugins/letsencrypt_example_plugins.py b/examples/plugins/certbot_example_plugins.py similarity index 82% rename from examples/plugins/letsencrypt_example_plugins.py rename to examples/plugins/certbot_example_plugins.py index 5c22ca7ff..9dec2e108 100644 --- a/examples/plugins/letsencrypt_example_plugins.py +++ b/examples/plugins/certbot_example_plugins.py @@ -1,12 +1,12 @@ -"""Example Let's Encrypt plugins. +"""Example Certbot plugins. -For full examples, see `letsencrypt.plugins`. +For full examples, see `certbot.plugins`. """ import zope.interface -from letsencrypt import interfaces -from letsencrypt.plugins import common +from certbot import interfaces +from certbot.plugins import common @zope.interface.implementer(interfaces.IAuthenticator) diff --git a/examples/plugins/setup.py b/examples/plugins/setup.py index 71bb95333..4538e83b8 100644 --- a/examples/plugins/setup.py +++ b/examples/plugins/setup.py @@ -2,16 +2,16 @@ from setuptools import setup setup( - name='letsencrypt-example-plugins', - package='letsencrypt_example_plugins.py', + name='certbot-example-plugins', + package='certbot_example_plugins.py', install_requires=[ - 'letsencrypt', + 'certbot', 'zope.interface', ], entry_points={ - 'letsencrypt.plugins': [ - 'example_authenticator = letsencrypt_example_plugins:Authenticator', - 'example_installer = letsencrypt_example_plugins:Installer', + 'certbot.plugins': [ + 'example_authenticator = certbot_example_plugins:Authenticator', + 'example_installer = certbot_example_plugins:Installer', ], }, ) diff --git a/linter_plugin.py b/linter_plugin.py index 9a165d81f..4938755cf 100644 --- a/linter_plugin.py +++ b/linter_plugin.py @@ -1,4 +1,4 @@ -"""Let's Encrypt ACME PyLint plugin. +"""Certbot ACME PyLint plugin. http://docs.pylint.org/plugins.html diff --git a/pep8.travis.sh b/pep8.travis.sh index fe8f84639..c13547a78 100755 --- a/pep8.travis.sh +++ b/pep8.travis.sh @@ -8,10 +8,10 @@ pep8 --config=acme/.pep8 acme pep8 \ setup.py \ certbot \ - letsencrypt-apache \ - letsencrypt-nginx \ - letsencrypt-compatibility-test \ - letshelp-letsencrypt \ + certbot-apache \ + certbot-nginx \ + certbot-compatibility-test \ + letshelp-certbot \ || echo "PEP8 checking failed, but it's ignored in Travis" # echo exits with 0 diff --git a/tests/boulder-integration.sh b/tests/boulder-integration.sh index 77e866b52..46f43ec7c 100755 --- a/tests/boulder-integration.sh +++ b/tests/boulder-integration.sh @@ -21,7 +21,7 @@ else fi common_no_force_renew() { - letsencrypt_test_no_force_renew \ + certbot_test_no_force_renew \ --authenticator standalone \ --installer null \ "$@" @@ -94,5 +94,5 @@ common revoke --cert-path "$root/conf/live/le2.wtf/cert.pem" \ if type nginx; then - . ./letsencrypt-nginx/tests/boulder-integration.sh + . ./certbot-nginx/tests/boulder-integration.sh fi diff --git a/tools/release.sh b/tools/release.sh index 7e67d4e4c..d41192af9 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -28,7 +28,7 @@ if [ "$1" = "--production" ] ; then CheckVersion "Next version" "$nextversion" RELEASE_BRANCH="candidate-$version" else - version=`grep "__version__" letsencrypt/__init__.py | cut -d\' -f2 | sed s/\.dev0//` + version=`grep "__version__" certbot/__init__.py | cut -d\' -f2 | sed s/\.dev0//` version="$version.dev$(date +%Y%m%d)1" RELEASE_BRANCH="dev-release" echo Releasing developer version "$version"... @@ -45,10 +45,10 @@ export GPG_TTY=$(tty) PORT=${PORT:-1234} # subpackages to be released -SUBPKGS=${SUBPKGS:-"acme letsencrypt-apache letsencrypt-nginx letshelp-letsencrypt"} +SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot"} subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)" -# letsencrypt_compatibility_test is not packaged because: -# - it is not meant to be used by anyone else than Let's Encrypt devs +# certbot_compatibility_test is not packaged because: +# - it is not meant to be used by anyone else than Certbot devs # - it causes problems when running nosetests - the latter tries to # run everything that matches test*, while there are no unittests # there @@ -83,14 +83,14 @@ git checkout "$RELEASE_BRANCH" SetVersion() { ver="$1" - for pkg_dir in $SUBPKGS letsencrypt-compatibility-test + for pkg_dir in $SUBPKGS certbot-compatibility-test do sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py done - sed -i "s/^__version.*/__version__ = '$ver'/" letsencrypt/__init__.py + sed -i "s/^__version.*/__version__ = '$ver'/" certbot/__init__.py # interactive user input - git add -p letsencrypt $SUBPKGS letsencrypt-compatibility-test + git add -p certbot $SUBPKGS certbot-compatibility-test } @@ -117,7 +117,7 @@ done mkdir "dist.$version" -mv dist "dist.$version/letsencrypt" +mv dist "dist.$version/certbot" for pkg_dir in $SUBPKGS do mv $pkg_dir/dist "dist.$version/$pkg_dir/" @@ -140,7 +140,7 @@ pip install -U pip pip install \ --no-cache-dir \ --extra-index-url http://localhost:$PORT \ - letsencrypt $SUBPKGS + certbot $SUBPKGS # stop local PyPI kill $! cd ~- @@ -155,14 +155,14 @@ mkdir ../kgs kgs="../kgs/$version" pip freeze | tee $kgs pip install nose -for module in letsencrypt $subpkgs_modules ; do +for module in certbot $subpkgs_modules ; do echo testing $module nosetests $module done deactivate # pin pip hashes of the things we just built -for pkg in acme letsencrypt letsencrypt-apache ; do +for pkg in acme certbot certbot-apache ; do echo $pkg==$version \\ pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),' done > /tmp/hashes.$$ diff --git a/tools/venv.sh b/tools/venv.sh index 82712d33e..a9cac9cf1 100755 --- a/tools/venv.sh +++ b/tools/venv.sh @@ -1,5 +1,5 @@ #!/bin/sh -xe -# Developer virtualenv setup for Let's Encrypt client +# Developer virtualenv setup for Certbot client export VENV_ARGS="--python python2" diff --git a/tools/venv3.sh b/tools/venv3.sh index 645ed0d47..35ffac749 100755 --- a/tools/venv3.sh +++ b/tools/venv3.sh @@ -1,5 +1,5 @@ #!/bin/sh -xe -# Developer Python3 virtualenv setup for Let's Encrypt +# Developer Python3 virtualenv setup for Certbot export VENV_NAME="${VENV_NAME:-venv3}" export VENV_ARGS="--python python3" From 21173e2353595bc592571162961a9bfb89ad57f2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 10:50:31 -0700 Subject: [PATCH 221/317] Partial le-auto rename --- letsencrypt-auto-source/Dockerfile | 6 +++--- letsencrypt-auto-source/build.py | 10 +++++----- letsencrypt-auto-source/letsencrypt-auto.template | 12 ++++++------ .../pieces/bootstrappers/deb_common.sh | 2 +- .../pieces/letsencrypt-auto-requirements.txt | 2 +- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/letsencrypt-auto-source/Dockerfile b/letsencrypt-auto-source/Dockerfile index ad2465fda..23e8f26de 100644 --- a/letsencrypt-auto-source/Dockerfile +++ b/letsencrypt-auto-source/Dockerfile @@ -17,16 +17,16 @@ RUN apt-get update && \ apt-get clean RUN pip install nose -RUN mkdir -p /home/lea/letsencrypt +RUN mkdir -p /home/lea/certbot # Install fake testing CA: COPY ./tests/certs/ca/my-root-ca.crt.pem /usr/local/share/ca-certificates/ RUN update-ca-certificates # Copy code: -COPY . /home/lea/letsencrypt/letsencrypt-auto-source +COPY . /home/lea/certbot/letsencrypt-auto-source USER lea WORKDIR /home/lea -CMD ["nosetests", "-v", "-s", "letsencrypt/letsencrypt-auto-source/tests"] +CMD ["nosetests", "-v", "-s", "certbot/letsencrypt-auto-source/tests"] diff --git a/letsencrypt-auto-source/build.py b/letsencrypt-auto-source/build.py index 9a5fc46a7..ea74f9766 100755 --- a/letsencrypt-auto-source/build.py +++ b/letsencrypt-auto-source/build.py @@ -14,11 +14,11 @@ from sys import argv DIR = dirname(abspath(__file__)) -def le_version(build_script_dir): - """Return the version number stamped in letsencrypt/__init__.py.""" +def certbot_version(build_script_dir): + """Return the version number stamped in certbot/__init__.py.""" return re.search('''^__version__ = ['"](.+)['"].*''', file_contents(join(dirname(build_script_dir), - 'letsencrypt', + 'certbot', '__init__.py')), re.M).group(1) @@ -32,13 +32,13 @@ def build(version=None, requirements=None): """Return the built contents of the letsencrypt-auto script. :arg version: The version to attach to the script. Default: the version of - the letsencrypt package + the certbot package :arg requirements: The contents of the requirements file to embed. Default: contents of letsencrypt-auto-requirements.txt """ special_replacements = { - 'LE_AUTO_VERSION': version or le_version(DIR) + 'LE_AUTO_VERSION': version or certbot_version(DIR) } if requirements: special_replacements['letsencrypt-auto-requirements.txt'] = requirements diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 526a03fae..2c8e1ec4c 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -1,6 +1,6 @@ #!/bin/sh # -# Download and run the latest release version of the Let's Encrypt client. +# Download and run the latest release version of the Certbot client. # # NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING # @@ -46,7 +46,7 @@ for arg in "$@" ; do done # letsencrypt-auto needs root access to bootstrap OS dependencies, and -# letsencrypt itself needs root access for almost all modes of operation +# certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` @@ -157,7 +157,7 @@ Bootstrap() { elif grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon else - echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" @@ -219,7 +219,7 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run letsencrypt..." + echo "Requesting root privileges to run certbot..." echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else @@ -227,8 +227,8 @@ else # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the letsencrypt - # package). Phase 2 checks the version of the locally installed letsencrypt. + # letsencrypt-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then # If it looks like we've never bootstrapped before, bootstrap: diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index bbafb39d7..57ed11399 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -69,7 +69,7 @@ BootstrapDebCommon() { AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" else echo "No libaugeas0 version is available that's new enough to run the" - echo "Let's Encrypt apache plugin..." + echo "Certbot apache plugin..." fi # XXX add a case for ubuntu PPAs fi diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index bc4a0bebe..27cfb3d43 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -1,5 +1,5 @@ # This is the flattened list of packages letsencrypt-auto installs. To generate -# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. argparse==1.4.0 \ From 6daa2dd0429ca2f4e392f4e3ae710b2152cd0034 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 13:44:12 -0700 Subject: [PATCH 222/317] Doc checkup --- docs/api/account.rst | 4 ++-- docs/api/achallenges.rst | 4 ++-- docs/api/auth_handler.rst | 4 ++-- docs/api/client.rst | 4 ++-- docs/api/configuration.rst | 4 ++-- docs/api/constants.rst | 4 ++-- docs/api/continuity_auth.rst | 5 ----- docs/api/crypto_util.rst | 4 ++-- docs/api/display.rst | 16 ++++++++-------- docs/api/errors.rst | 4 ++-- docs/api/index.rst | 4 ++-- docs/api/interfaces.rst | 4 ++-- docs/api/le_util.rst | 4 ++-- docs/api/log.rst | 4 ++-- docs/api/plugins/common.rst | 4 ++-- docs/api/plugins/disco.rst | 4 ++-- docs/api/plugins/manual.rst | 4 ++-- docs/api/plugins/standalone.rst | 4 ++-- docs/api/plugins/util.rst | 4 ++-- docs/api/plugins/webroot.rst | 4 ++-- docs/api/proof_of_possession.rst | 5 ----- docs/api/reporter.rst | 4 ++-- docs/api/reverter.rst | 4 ++-- docs/api/storage.rst | 4 ++-- docs/conf.py | 6 +++--- docs/man/certbot.rst | 1 + docs/man/letsencrypt.rst | 1 - 27 files changed, 54 insertions(+), 64 deletions(-) delete mode 100644 docs/api/continuity_auth.rst delete mode 100644 docs/api/proof_of_possession.rst create mode 100644 docs/man/certbot.rst delete mode 100644 docs/man/letsencrypt.rst diff --git a/docs/api/account.rst b/docs/api/account.rst index 16c2061a8..fd90230ea 100644 --- a/docs/api/account.rst +++ b/docs/api/account.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.account` +:mod:`certbot.account` -------------------------- -.. automodule:: letsencrypt.account +.. automodule:: certbot.account :members: diff --git a/docs/api/achallenges.rst b/docs/api/achallenges.rst index 09cec1702..90dda3f06 100644 --- a/docs/api/achallenges.rst +++ b/docs/api/achallenges.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.achallenges` +:mod:`certbot.achallenges` ------------------------------ -.. automodule:: letsencrypt.achallenges +.. automodule:: certbot.achallenges :members: diff --git a/docs/api/auth_handler.rst b/docs/api/auth_handler.rst index 3b168faf8..8819bb1bd 100644 --- a/docs/api/auth_handler.rst +++ b/docs/api/auth_handler.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.auth_handler` +:mod:`certbot.auth_handler` ------------------------------- -.. automodule:: letsencrypt.auth_handler +.. automodule:: certbot.auth_handler :members: diff --git a/docs/api/client.rst b/docs/api/client.rst index 7fe44df50..00a443cd9 100644 --- a/docs/api/client.rst +++ b/docs/api/client.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.client` +:mod:`certbot.client` ------------------------- -.. automodule:: letsencrypt.client +.. automodule:: certbot.client :members: diff --git a/docs/api/configuration.rst b/docs/api/configuration.rst index e92392b99..4e99c73d2 100644 --- a/docs/api/configuration.rst +++ b/docs/api/configuration.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.configuration` +:mod:`certbot.configuration` -------------------------------- -.. automodule:: letsencrypt.configuration +.. automodule:: certbot.configuration :members: diff --git a/docs/api/constants.rst b/docs/api/constants.rst index 3a2815b5e..e225056a2 100644 --- a/docs/api/constants.rst +++ b/docs/api/constants.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.constants` +:mod:`certbot.constants` ----------------------------------- -.. automodule:: letsencrypt.constants +.. automodule:: certbot.constants :members: diff --git a/docs/api/continuity_auth.rst b/docs/api/continuity_auth.rst deleted file mode 100644 index 82869e6f4..000000000 --- a/docs/api/continuity_auth.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.continuity_auth` ----------------------------------- - -.. automodule:: letsencrypt.continuity_auth - :members: diff --git a/docs/api/crypto_util.rst b/docs/api/crypto_util.rst index 5d4c77538..2f473944c 100644 --- a/docs/api/crypto_util.rst +++ b/docs/api/crypto_util.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.crypto_util` +:mod:`certbot.crypto_util` ------------------------------ -.. automodule:: letsencrypt.crypto_util +.. automodule:: certbot.crypto_util :members: diff --git a/docs/api/display.rst b/docs/api/display.rst index 117a91708..1a18e6534 100644 --- a/docs/api/display.rst +++ b/docs/api/display.rst @@ -1,23 +1,23 @@ -:mod:`letsencrypt.display` +:mod:`certbot.display` -------------------------- -.. automodule:: letsencrypt.display +.. automodule:: certbot.display :members: -:mod:`letsencrypt.display.util` +:mod:`certbot.display.util` =============================== -.. automodule:: letsencrypt.display.util +.. automodule:: certbot.display.util :members: -:mod:`letsencrypt.display.ops` +:mod:`certbot.display.ops` ============================== -.. automodule:: letsencrypt.display.ops +.. automodule:: certbot.display.ops :members: -:mod:`letsencrypt.display.enhancements` +:mod:`certbot.display.enhancements` ======================================= -.. automodule:: letsencrypt.display.enhancements +.. automodule:: certbot.display.enhancements :members: diff --git a/docs/api/errors.rst b/docs/api/errors.rst index 1ad13235c..a9324765b 100644 --- a/docs/api/errors.rst +++ b/docs/api/errors.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.errors` +:mod:`certbot.errors` ------------------------- -.. automodule:: letsencrypt.errors +.. automodule:: certbot.errors :members: diff --git a/docs/api/index.rst b/docs/api/index.rst index a2475eeae..be94214c9 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt` +:mod:`certbot` ------------------ -.. automodule:: letsencrypt +.. automodule:: certbot :members: diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst index 00b0a1e50..2988b3b87 100644 --- a/docs/api/interfaces.rst +++ b/docs/api/interfaces.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.interfaces` +:mod:`certbot.interfaces` ----------------------------- -.. automodule:: letsencrypt.interfaces +.. automodule:: certbot.interfaces :members: diff --git a/docs/api/le_util.rst b/docs/api/le_util.rst index 8c6b717cf..c9e332745 100644 --- a/docs/api/le_util.rst +++ b/docs/api/le_util.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.le_util` +:mod:`certbot.le_util` -------------------------- -.. automodule:: letsencrypt.le_util +.. automodule:: certbot.le_util :members: diff --git a/docs/api/log.rst b/docs/api/log.rst index f41c6c4b1..41311de90 100644 --- a/docs/api/log.rst +++ b/docs/api/log.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.log` +:mod:`certbot.log` ---------------------- -.. automodule:: letsencrypt.log +.. automodule:: certbot.log :members: diff --git a/docs/api/plugins/common.rst b/docs/api/plugins/common.rst index ca55ba8fb..7cfaf8d70 100644 --- a/docs/api/plugins/common.rst +++ b/docs/api/plugins/common.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.common` +:mod:`certbot.plugins.common` --------------------------------- -.. automodule:: letsencrypt.plugins.common +.. automodule:: certbot.plugins.common :members: diff --git a/docs/api/plugins/disco.rst b/docs/api/plugins/disco.rst index 7bf2b76b4..1a27f0f69 100644 --- a/docs/api/plugins/disco.rst +++ b/docs/api/plugins/disco.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.disco` +:mod:`certbot.plugins.disco` -------------------------------- -.. automodule:: letsencrypt.plugins.disco +.. automodule:: certbot.plugins.disco :members: diff --git a/docs/api/plugins/manual.rst b/docs/api/plugins/manual.rst index 4661ab7df..eea443499 100644 --- a/docs/api/plugins/manual.rst +++ b/docs/api/plugins/manual.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.manual` +:mod:`certbot.plugins.manual` --------------------------------- -.. automodule:: letsencrypt.plugins.manual +.. automodule:: certbot.plugins.manual :members: diff --git a/docs/api/plugins/standalone.rst b/docs/api/plugins/standalone.rst index f5b9d9c24..60aa48b4f 100644 --- a/docs/api/plugins/standalone.rst +++ b/docs/api/plugins/standalone.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.standalone` +:mod:`certbot.plugins.standalone` ------------------------------------- -.. automodule:: letsencrypt.plugins.standalone +.. automodule:: certbot.plugins.standalone :members: diff --git a/docs/api/plugins/util.rst b/docs/api/plugins/util.rst index 6bc8995db..30ab3d49f 100644 --- a/docs/api/plugins/util.rst +++ b/docs/api/plugins/util.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.util` +:mod:`certbot.plugins.util` ------------------------------- -.. automodule:: letsencrypt.plugins.util +.. automodule:: certbot.plugins.util :members: diff --git a/docs/api/plugins/webroot.rst b/docs/api/plugins/webroot.rst index 339d546a5..e1f4523f7 100644 --- a/docs/api/plugins/webroot.rst +++ b/docs/api/plugins/webroot.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.plugins.webroot` +:mod:`certbot.plugins.webroot` ---------------------------------- -.. automodule:: letsencrypt.plugins.webroot +.. automodule:: certbot.plugins.webroot :members: diff --git a/docs/api/proof_of_possession.rst b/docs/api/proof_of_possession.rst deleted file mode 100644 index db8c6c563..000000000 --- a/docs/api/proof_of_possession.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`letsencrypt.proof_of_possession` --------------------------------------- - -.. automodule:: letsencrypt.proof_of_possession - :members: diff --git a/docs/api/reporter.rst b/docs/api/reporter.rst index 03260f9cd..ad71dbb69 100644 --- a/docs/api/reporter.rst +++ b/docs/api/reporter.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.reporter` +:mod:`certbot.reporter` --------------------------- -.. automodule:: letsencrypt.reporter +.. automodule:: certbot.reporter :members: diff --git a/docs/api/reverter.rst b/docs/api/reverter.rst index 4c220124f..3e0ac750b 100644 --- a/docs/api/reverter.rst +++ b/docs/api/reverter.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.reverter` +:mod:`certbot.reverter` --------------------------- -.. automodule:: letsencrypt.reverter +.. automodule:: certbot.reverter :members: diff --git a/docs/api/storage.rst b/docs/api/storage.rst index 198d85b46..34e3a45c0 100644 --- a/docs/api/storage.rst +++ b/docs/api/storage.rst @@ -1,5 +1,5 @@ -:mod:`letsencrypt.storage` +:mod:`certbot.storage` -------------------------- -.. automodule:: letsencrypt.storage +.. automodule:: certbot.storage :members: diff --git a/docs/conf.py b/docs/conf.py index 739d6ee43..0f2938202 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ import sys here = os.path.abspath(os.path.dirname(__file__)) # read version number (and other metadata) from package init -init_fn = os.path.join(here, '..', 'letsencrypt', '__init__.py') +init_fn = os.path.join(here, '..', 'certbot', '__init__.py') with codecs.open(init_fn, encoding='utf8') as fd: meta = dict(re.findall(r"""__([a-z]+)__ = '([^']+)""", fd.read())) @@ -277,9 +277,9 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'letsencrypt', u'Let\'s Encrypt Documentation', + ('index', 'certbot', u'Let\'s Encrypt Documentation', [project], 7), - ('man/letsencrypt', 'letsencrypt', u'letsencrypt script documentation', + ('man/certbot', 'certbot', u'certbot script documentation', [project], 1), ] diff --git a/docs/man/certbot.rst b/docs/man/certbot.rst new file mode 100644 index 000000000..7382d7811 --- /dev/null +++ b/docs/man/certbot.rst @@ -0,0 +1 @@ +.. program-output:: certbot --help all diff --git a/docs/man/letsencrypt.rst b/docs/man/letsencrypt.rst deleted file mode 100644 index 30f33c890..000000000 --- a/docs/man/letsencrypt.rst +++ /dev/null @@ -1 +0,0 @@ -.. program-output:: letsencrypt --help all From 39763bc69f68178dbd5fad5d57e1f6a0f65ce957 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 13:45:36 -0700 Subject: [PATCH 223/317] Stray Let's Encrypt --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 0f2938202..fb2bdea73 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Let's Encrypt documentation build configuration file, created by +# Certbot documentation build configuration file, created by # sphinx-quickstart on Sun Nov 23 20:35:21 2014. # # This file is execfile()d with the current directory set to its From 7472812eddbaeea18b12b8a2cd03b5a77e9fd448 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 14:01:25 -0700 Subject: [PATCH 224/317] Reverted bad path change --- .../tests/apache-conf-files/passing/finalize-1243.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf index 0b34ee5f0..0918e5669 100644 --- a/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/finalize-1243.conf @@ -41,7 +41,7 @@ Listen 443 SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key SSLCertificateChainFile /etc/ssl/certs/ssl-cert-snakeoil.pem -Include /etc/certbot/options-ssl-apache.conf +Include /etc/letsencrypt/options-ssl-apache.conf From a64d8b7e006bcb6aa7d2f9023e6f62292082a69f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 15:16:48 -0700 Subject: [PATCH 225/317] Update contributing instructions --- docs/contributing.rst | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 69604780c..216cd4888 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -36,7 +36,7 @@ client by typing: .. code-block:: shell - letsencrypt + certbot Activating a shell in this way makes it easier to run unit tests with ``tox`` and integration tests, as described below. To reverse this, you @@ -97,7 +97,7 @@ Generally it is sufficient to open a pull request and let Github and Travis run integration tests for you. However, if you prefer to run tests, you can use Vagrant, using the Vagrantfile -in Let's Encrypt's repository. To execute the tests on a Vagrant box, the only +in Certbot's repository. To execute the tests on a Vagrant box, the only command you are required to run is:: ./tests/boulder-integration.sh @@ -141,9 +141,9 @@ and ``nginx.wtf`` to 127.0.0.1. You may now run (in a separate terminal):: ./tests/boulder-integration.sh && echo OK || echo FAIL -If you would like to test `letsencrypt_nginx` plugin (highly +If you would like to test `certbot_nginx` plugin (highly encouraged) make sure to install prerequisites as listed in -``letsencrypt-nginx/tests/boulder-integration.sh`` and rerun +``certbot-nginx/tests/boulder-integration.sh`` and rerun the integration tests suite. .. _Boulder: https://github.com/letsencrypt/boulder @@ -155,28 +155,28 @@ Code components and layout acme contains all protocol specific code -letsencrypt +certbot all client code Plugin-architecture ------------------- -Let's Encrypt has a plugin architecture to facilitate support for +Certbot has a plugin architecture to facilitate support for different webservers, other TLS servers, and operating systems. The interfaces available for plugins to implement are defined in `interfaces.py`_ and `plugins/common.py`_. The most common kind of plugin is a "Configurator", which is likely to -implement the `~letsencrypt.interfaces.IAuthenticator` and -`~letsencrypt.interfaces.IInstaller` interfaces (though some +implement the `~certbot.interfaces.IAuthenticator` and +`~certbot.interfaces.IInstaller` interfaces (though some Configurators may implement just one of those). -There are also `~letsencrypt.interfaces.IDisplay` plugins, +There are also `~certbot.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/interfaces.py -.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/plugins/common.py#L34 +.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/interfaces.py +.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/plugins/common.py#L34 Authenticators @@ -232,7 +232,7 @@ Installer Development --------------------- There are a few existing classes that may be beneficial while -developing a new `~letsencrypt.interfaces.IInstaller`. +developing a new `~certbot.interfaces.IInstaller`. Installers aimed to reconfigure UNIX servers may use Augeas for configuration parsing and can inherit from `~.AugeasConfigurator` class to handle much of the interface. Installers that are unable to use @@ -244,7 +244,7 @@ Display ~~~~~~~ We currently offer a pythondialog and "text" mode for displays. Display -plugins implement the `~letsencrypt.interfaces.IDisplay` +plugins implement the `~certbot.interfaces.IDisplay` interface. .. _dev-plugin: @@ -252,10 +252,10 @@ interface. Writing your own plugin ======================= -Let's Encrypt client supports dynamic discovery of plugins through the +Certbot 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 +custom implementation of `~certbot.interfaces.IAuthenticator` or +the `~certbot.interfaces.IInstaller` without having to merge it with the core upstream source code. An example is provided in ``examples/plugins/`` directory. @@ -345,7 +345,7 @@ Other methods for running the client Vagrant ------- -If you are a Vagrant user, Let's Encrypt comes with a Vagrantfile that +If you are a Vagrant user, Certbot 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: @@ -354,7 +354,7 @@ synced to ``/vagrant``, so you can get started with: vagrant ssh cd /vagrant - sudo ./venv/bin/letsencrypt + sudo ./venv/bin/certbot Support for other Linux distributions coming soon. @@ -373,19 +373,19 @@ 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``) +development. Certbot 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 . + docker build -t certbot -f Dockerfile-dev . Now run tests inside the Docker image: .. code-block:: shell - docker run -it letsencrypt bash + docker run -it certbot bash cd src tox -e py27 From 3a975ac580fa5492875122c19dfb1083a30b4fe0 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 16:27:22 -0700 Subject: [PATCH 226/317] Use command to find certbot path --- .../certbot_apache/tests/apache-conf-files/apache-conf-test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test b/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test index b2a453fb9..44268cb8f 100755 --- a/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/apache-conf-test @@ -59,7 +59,7 @@ trap CleanupExit INT for f in *.conf ; do echo -n testing "$f"... Setup - RESULT=`echo c | sudo env "PATH=$PATH" certbot -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` + RESULT=`echo c | sudo $(command -v certbot) -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1` if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then echo passed else From b956a968c6727f488984c47a86dd921d03335167 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 16:56:02 -0700 Subject: [PATCH 227/317] this commit was authored by the Certbot Project --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-compatibility-test/setup.py | 2 +- certbot-nginx/setup.py | 2 +- letshelp-certbot/setup.py | 2 +- setup.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 72c751dca..cbd3bfb87 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -53,7 +53,7 @@ setup( version=version, description='ACME protocol implementation in Python', url='https://github.com/letsencrypt/letsencrypt', - author="Electronic Frontier Foundation", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 89373800c..7358c7041 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -33,7 +33,7 @@ setup( version=version, description="Apache plugin for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Electronic Frontier Foundation", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index 51704fda5..c62a10f89 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -35,7 +35,7 @@ setup( version=version, description="Compatibility tests for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Electronic Frontier Foundation", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 7c07ff4a0..0e5c27a0a 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -33,7 +33,7 @@ setup( version=version, description="Nginx plugin for Certbot", url='https://github.com/letsencrypt/letsencrypt', - author="Electronic Frontier Foundation", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py index e625b288c..8359d2766 100644 --- a/letshelp-certbot/setup.py +++ b/letshelp-certbot/setup.py @@ -24,7 +24,7 @@ setup( version=version, description="Let's help Certbot client", url='https://github.com/letsencrypt/letsencrypt', - author="Electronic Frontier Foundation", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ diff --git a/setup.py b/setup.py index a21c43946..67cefdc48 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ setup( description="ACME client", long_description=readme, # later: + '\n\n' + changes url='https://github.com/letsencrypt/letsencrypt', - author="Electronic Frontier Foundation", + author="Certbot Project", author_email='client-dev@letsencrypt.org', license='Apache License 2.0', classifiers=[ From 75a1d81458ce948f067c4b97277bca4a97c53c60 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 17:04:23 -0700 Subject: [PATCH 228/317] More stray ncrypt reference cleanup --- Dockerfile | 2 +- Dockerfile-dev | 2 +- LICENSE.txt | 2 +- certbot-apache/certbot_apache/configurator.py | 4 ++-- certbot-apache/docs/conf.py | 4 ++-- certbot-compatibility-test/certbot_compatibility_test/util.py | 2 +- certbot-compatibility-test/docs/conf.py | 4 ++-- certbot/client.py | 2 +- letshelp-certbot/docs/conf.py | 4 ++-- tests/boulder-integration.sh | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Dockerfile b/Dockerfile index b996558b4..3e4c9430e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ MAINTAINER William Budington EXPOSE 443 # TODO: make sure --config-dir and --work-dir cannot be changed -# through the CLI (letsencrypt-docker wrapper that uses standalone +# through the CLI (certbot-docker wrapper that uses standalone # authenticator and text mode only?) VOLUME /etc/letsencrypt /var/lib/letsencrypt diff --git a/Dockerfile-dev b/Dockerfile-dev index bfae9b087..c7e1d7b2e 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -9,7 +9,7 @@ MAINTAINER Yan EXPOSE 443 # TODO: make sure --config-dir and --work-dir cannot be changed -# through the CLI (letsencrypt-docker wrapper that uses standalone +# through the CLI (certbot-docker wrapper that uses standalone # authenticator and text mode only?) VOLUME /etc/letsencrypt /var/lib/letsencrypt diff --git a/LICENSE.txt b/LICENSE.txt index 5965ec2ef..b905dd120 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Let's Encrypt Python Client +Certbot ACME Client Copyright (c) Electronic Frontier Foundation and others Licensed Apache Version 2.0 diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 0873edd24..26c3185be 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -1083,7 +1083,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): "redirection") self._create_redirect_vhost(ssl_vhost) else: - # Check if LetsEncrypt redirection already exists + # Check if Certbot redirection already exists self._verify_no_certbot_redirect(general_vh) # Note: if code flow gets here it means we didn't find the exact @@ -1125,7 +1125,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """Checks to see if a redirect was already installed by certbot. Checks to see if virtualhost already contains a rewrite rule that is - identical to Letsencrypt's redirection rewrite rule. + identical to Certbot's redirection rewrite rule. :param vhost: vhost to check :type vhost: :class:`~certbot_apache.obj.VirtualHost` diff --git a/certbot-apache/docs/conf.py b/certbot-apache/docs/conf.py index b7faa7b26..2f996c7f4 100644 --- a/certbot-apache/docs/conf.py +++ b/certbot-apache/docs/conf.py @@ -67,7 +67,7 @@ master_doc = 'index' # General information about the project. project = u'certbot-apache' copyright = u'2014-2015, Let\'s Encrypt Project' -author = u'Let\'s Encrypt Project' +author = u'Certbot 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 @@ -250,7 +250,7 @@ latex_elements = { # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'certbot-apache.tex', u'certbot-apache Documentation', - u'Let\'s Encrypt Project', 'manual'), + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/certbot-compatibility-test/certbot_compatibility_test/util.py b/certbot-compatibility-test/certbot_compatibility_test/util.py index 8ff9c8dd3..cbce4fb56 100644 --- a/certbot-compatibility-test/certbot_compatibility_test/util.py +++ b/certbot-compatibility-test/certbot_compatibility_test/util.py @@ -1,4 +1,4 @@ -"""Utility functions for Let"s Encrypt plugin tests.""" +"""Utility functions for Certbot plugin tests.""" import argparse import copy import contextlib diff --git a/certbot-compatibility-test/docs/conf.py b/certbot-compatibility-test/docs/conf.py index 6aea7121e..1ef69ab2d 100644 --- a/certbot-compatibility-test/docs/conf.py +++ b/certbot-compatibility-test/docs/conf.py @@ -61,7 +61,7 @@ master_doc = 'index' # General information about the project. project = u'certbot-compatibility-test' copyright = u'2014-2015, Let\'s Encrypt Project' -author = u'Let\'s Encrypt Project' +author = u'Certbot 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 @@ -245,7 +245,7 @@ latex_elements = { latex_documents = [ (master_doc, 'certbot-compatibility-test.tex', u'certbot-compatibility-test Documentation', - u'Let\'s Encrypt Project', 'manual'), + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/certbot/client.py b/certbot/client.py index 71d753e42..60e37a787 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -51,7 +51,7 @@ def _determine_user_agent(config): """ if config.user_agent is None: - ua = "LetsEncryptPythonClient/{0} ({1}) Authenticator/{2} Installer/{3}" + ua = "CertbotACMEClient/{0} ({1}) Authenticator/{2} Installer/{3}" ua = ua.format(certbot.__version__, " ".join(le_util.get_os_info()), config.authenticator, config.installer) else: diff --git a/letshelp-certbot/docs/conf.py b/letshelp-certbot/docs/conf.py index ba669113a..905d70662 100644 --- a/letshelp-certbot/docs/conf.py +++ b/letshelp-certbot/docs/conf.py @@ -60,7 +60,7 @@ master_doc = 'index' # General information about the project. project = u'letshelp-certbot' copyright = u'2014-2015, Let\'s Encrypt Project' -author = u'Let\'s Encrypt Project' +author = u'Certbot 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 @@ -243,7 +243,7 @@ latex_elements = { # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'letshelp-certbot.tex', u'letshelp-certbot Documentation', - u'Let\'s Encrypt Project', 'manual'), + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of diff --git a/tests/boulder-integration.sh b/tests/boulder-integration.sh index 46f43ec7c..201343525 100755 --- a/tests/boulder-integration.sh +++ b/tests/boulder-integration.sh @@ -4,7 +4,7 @@ # instance (see ./boulder-start.sh). # # Environment variables: -# SERVER: Passed as "letsencrypt --server" argument. +# SERVER: Passed as "certbot --server" argument. # # Note: this script is called by Boulder integration test suite! From b4f6ed84708510adeb427b5e9301c13b48a23047 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 17:10:27 -0700 Subject: [PATCH 229/317] rename letstest stuff --- tests/letstest/README.md | 6 ++--- tests/letstest/multitester.py | 22 +++++++++---------- tests/letstest/scripts/test_apache2.sh | 8 +++---- .../letstest/scripts/test_renew_standalone.sh | 6 ++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/letstest/README.md b/tests/letstest/README.md index a085e9d91..a9b4db6b5 100644 --- a/tests/letstest/README.md +++ b/tests/letstest/README.md @@ -1,10 +1,10 @@ # letstest -simple aws testfarm scripts for letsencrypt client testing +simple aws testfarm scripts for certbot client testing - Configures (canned) boulder server - Launches EC2 instances with a given list of AMIs for different distros -- Copies letsencrypt repo and puts it on the instances -- Runs letsencrypt tests (bash scripts) on all of these +- Copies certbot repo and puts it on the instances +- Runs certbot tests (bash scripts) on all of these - Logs execution and success/fail for debugging ## Notes diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 876b7807f..aa5bf0efa 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -3,8 +3,8 @@ Letsencrypt Integration Test Tool - Configures (canned) boulder server - Launches EC2 instances with a given list of AMIs for different distros -- Copies letsencrypt repo and puts it on the instances -- Runs letsencrypt tests (bash scripts) on all of these +- Copies certbot repo and puts it on the instances +- Runs certbot tests (bash scripts) on all of these - Logs execution and success/fail for debugging Notes: @@ -61,10 +61,10 @@ parser.add_argument('test_script', # required=False) parser.add_argument('--repo', default='https://github.com/letsencrypt/letsencrypt.git', - help='letsencrypt git repo to use') + help='certbot git repo to use') parser.add_argument('--branch', default='~', - help='letsencrypt git branch to trial') + help='certbot git branch to trial') parser.add_argument('--pull_request', default='~', help='letsencrypt/letsencrypt pull request to trial') @@ -291,7 +291,7 @@ def config_and_launch_boulder(instance): execute(deploy_script, 'scripts/boulder_config.sh') execute(run_boulder) -def install_and_launch_letsencrypt(instance, boulder_url, target): +def install_and_launch_certbot(instance, boulder_url, target): execute(local_repo_to_remote) with shell_env(BOULDER_URL=boulder_url, PUBLIC_IP=instance.public_ip_address, @@ -301,13 +301,13 @@ def install_and_launch_letsencrypt(instance, boulder_url, target): OS_TYPE=target['type']): execute(deploy_script, cl_args.test_script) -def grab_letsencrypt_log(): +def grab_certbot_log(): "grabs letsencrypt.log via cat into logged stdout" sudo('if [ -f /var/log/letsencrypt/letsencrypt.log ]; then \ cat /var/log/letsencrypt/letsencrypt.log; else echo "[novarlog]"; fi') # fallback file if /var/log is unwriteable...? correct? - sudo('if [ -f ./letsencrypt.log ]; then \ - cat ./letsencrypt.log; else echo "[nolocallog]"; fi') + sudo('if [ -f ./certbot.log ]; then \ + cat ./certbot.log; else echo "[nolocallog]"; fi') def create_client_instances(targetlist): "Create a fleet of client instances" @@ -357,10 +357,10 @@ def test_client_process(inqueue, outqueue): print("%s - %s FAIL"%(target['ami'], target['name'])) pass - # append server letsencrypt.log to each per-machine output log - print("\n\nletsencrypt.log\n" + "-"*80 + "\n") + # append server certbot.log to each per-machine output log + print("\n\ncertbot.log\n" + "-"*80 + "\n") try: - execute(grab_letsencrypt_log) + execute(grab_certbot_log) except: print("log fail\n") pass diff --git a/tests/letstest/scripts/test_apache2.sh b/tests/letstest/scripts/test_apache2.sh index 940cc36c6..3e0846216 100755 --- a/tests/letstest/scripts/test_apache2.sh +++ b/tests/letstest/scripts/test_apache2.sh @@ -22,8 +22,8 @@ then sudo chmod -R oug+rwx /var/www sudo chmod -R oug+rw /etc/httpd sudo echo 'foobar' > /var/www/$PUBLIC_HOSTNAME/public_html/index.html - sudo mkdir /etc/httpd/sites-available #letsencrypt requires this... - sudo mkdir /etc/httpd/sites-enabled #letsencrypt requires this... + sudo mkdir /etc/httpd/sites-available #certbot requires this... + sudo mkdir /etc/httpd/sites-enabled #certbot requires this... #sudo echo "IncludeOptional sites-enabled/*.conf" >> /etc/httpd/conf/httpd.conf sudo echo """ @@ -35,7 +35,7 @@ then #sudo cp /etc/httpd/sites-available/$PUBLIC_HOSTNAME.conf /etc/httpd/sites-enabled/ fi -# Run letsencrypt-apache2. +# Run certbot-apache2. cd letsencrypt echo "Bootstrapping dependencies..." @@ -45,7 +45,7 @@ if [ $? -ne 0 ] ; then fi tools/venv.sh -sudo venv/bin/letsencrypt -v --debug --text --agree-dev-preview --agree-tos \ +sudo venv/bin/certbot -v --debug --text --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL if [ $? -ne 0 ] ; then diff --git a/tests/letstest/scripts/test_renew_standalone.sh b/tests/letstest/scripts/test_renew_standalone.sh index 9bce17a60..31c38ea46 100755 --- a/tests/letstest/scripts/test_renew_standalone.sh +++ b/tests/letstest/scripts/test_renew_standalone.sh @@ -3,7 +3,7 @@ # $OS_TYPE $PUBLIC_IP $PRIVATE_IP $PUBLIC_HOSTNAME $BOULDER_URL # are dynamically set at execution -# run letsencrypt-apache2 via letsencrypt-auto +# run certbot-apache2 via letsencrypt-auto cd letsencrypt export SUDO=sudo @@ -19,7 +19,7 @@ else fi bootstrap/dev/venv.sh -sudo venv/bin/letsencrypt certonly --debug --standalone -t --agree-dev-preview --agree-tos \ +sudo venv/bin/certbot certonly --debug --standalone -t --agree-dev-preview --agree-tos \ --renew-by-default --redirect --register-unsafely-without-email \ --domain $PUBLIC_HOSTNAME --server $BOULDER_URL -v if [ $? -ne 0 ] ; then @@ -36,7 +36,7 @@ if [ $? -ne 0 ] ; then FAIL=1 fi -sudo venv/bin/letsencrypt renew --renew-by-default +sudo venv/bin/certbot renew --renew-by-default if [ $? -ne 0 ] ; then FAIL=1 From ecc5e71761794935d24f23a7b38fbc08c46b5339 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 17:13:19 -0700 Subject: [PATCH 230/317] setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 1ea06661e..8d68bac30 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,6 +3,6 @@ zip_ok = false [nosetests] nocapture=1 -cover-package=letsencrypt,acme,letsencrypt_apache,letsencrypt_nginx +cover-package=certbot,acme,certbot_apache,certbot_nginx cover-erase=1 cover-tests=1 From e353f8fabc1c2daee151cca93a74fa2cb99336bd Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 14 Apr 2016 17:16:48 -0700 Subject: [PATCH 231/317] letstest is a Certbot integration test tool --- tests/letstest/multitester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index aa5bf0efa..0b9447e6a 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -1,5 +1,5 @@ """ -Letsencrypt Integration Test Tool +Certbot Integration Test Tool - Configures (canned) boulder server - Launches EC2 instances with a given list of AMIs for different distros From 7411cc4a253976ad43843d56ee556eaeb4778bec Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 10:11:57 -0700 Subject: [PATCH 232/317] Add letsencrypt shim --- letsencrypt/LICENSE.txt | 205 ++++++++++++++++++++++++++++ letsencrypt/MANIFEST.in | 2 + letsencrypt/README.rst | 2 + letsencrypt/letsencrypt/__init__.py | 8 ++ letsencrypt/setup.py | 62 +++++++++ 5 files changed, 279 insertions(+) create mode 100644 letsencrypt/LICENSE.txt create mode 100644 letsencrypt/MANIFEST.in create mode 100644 letsencrypt/README.rst create mode 100644 letsencrypt/letsencrypt/__init__.py create mode 100644 letsencrypt/setup.py diff --git a/letsencrypt/LICENSE.txt b/letsencrypt/LICENSE.txt new file mode 100644 index 000000000..82d868261 --- /dev/null +++ b/letsencrypt/LICENSE.txt @@ -0,0 +1,205 @@ +Let's Encrypt ACME Client +Copyright (c) Electronic Frontier Foundation and others +Licensed Apache Version 2.0 + +The nginx plugin incorporates code from nginxparser +Copyright (c) 2014 Fatih Erikli +Licensed MIT + + +Text of Apache License +====================== + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + +Text of MIT License +=================== +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/letsencrypt/MANIFEST.in b/letsencrypt/MANIFEST.in new file mode 100644 index 000000000..97e2ad3df --- /dev/null +++ b/letsencrypt/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE.txt +include README.rst diff --git a/letsencrypt/README.rst b/letsencrypt/README.rst new file mode 100644 index 000000000..b5fa0ec95 --- /dev/null +++ b/letsencrypt/README.rst @@ -0,0 +1,2 @@ +This package is a simple shim around the ``certbot`` ACME client for backwards +compatibility. diff --git a/letsencrypt/letsencrypt/__init__.py b/letsencrypt/letsencrypt/__init__.py new file mode 100644 index 000000000..a67d641f5 --- /dev/null +++ b/letsencrypt/letsencrypt/__init__.py @@ -0,0 +1,8 @@ +"""Let's Encrypt ACME client.""" +import sys + + +import certbot + + +sys.modules['letsencrypt'] = certbot diff --git a/letsencrypt/setup.py b/letsencrypt/setup.py new file mode 100644 index 000000000..708c31f4b --- /dev/null +++ b/letsencrypt/setup.py @@ -0,0 +1,62 @@ +import codecs +import os +import sys + +from setuptools import setup +from setuptools import find_packages + + +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() + + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +# This package is a simple shim around certbot +install_requires = ['certbot'] + + +version = '0.6.0.dev0' + + +setup( + name='letsencrypt', + version=version, + description="ACME client", + long_description=readme, + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Console', + 'Environment :: Console :: Curses', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + entry_points={ + 'console_scripts': [ + 'letsencrypt = certbot.main:main', + ], + }, +) From 0c1e2e33ec7621403b31943bca387dfb67fc1334 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 10:30:37 -0700 Subject: [PATCH 233/317] Add letsencrypt-apache shim --- letsencrypt-apache/LICENSE.txt | 190 ++++++++++++++++++ letsencrypt-apache/MANIFEST.in | 2 + letsencrypt-apache/README.rst | 2 + .../letsencrypt_apache/__init__.py | 8 + letsencrypt-apache/setup.py | 59 ++++++ 5 files changed, 261 insertions(+) create mode 100644 letsencrypt-apache/LICENSE.txt create mode 100644 letsencrypt-apache/MANIFEST.in create mode 100644 letsencrypt-apache/README.rst create mode 100644 letsencrypt-apache/letsencrypt_apache/__init__.py create mode 100644 letsencrypt-apache/setup.py diff --git a/letsencrypt-apache/LICENSE.txt b/letsencrypt-apache/LICENSE.txt new file mode 100644 index 000000000..981c46c9f --- /dev/null +++ b/letsencrypt-apache/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letsencrypt-apache/MANIFEST.in b/letsencrypt-apache/MANIFEST.in new file mode 100644 index 000000000..97e2ad3df --- /dev/null +++ b/letsencrypt-apache/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE.txt +include README.rst diff --git a/letsencrypt-apache/README.rst b/letsencrypt-apache/README.rst new file mode 100644 index 000000000..c0c201f14 --- /dev/null +++ b/letsencrypt-apache/README.rst @@ -0,0 +1,2 @@ +This package is a simple shim for backwards compatibility around +``certbot-apache``, the Apache plugin for ``certbot``. diff --git a/letsencrypt-apache/letsencrypt_apache/__init__.py b/letsencrypt-apache/letsencrypt_apache/__init__.py new file mode 100644 index 000000000..cc8faef21 --- /dev/null +++ b/letsencrypt-apache/letsencrypt_apache/__init__.py @@ -0,0 +1,8 @@ +"""Let's Encrypt Apache plugin.""" +import sys + + +import certbot_apache + + +sys.modules['letsencrypt_apache'] = certbot_apache diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py new file mode 100644 index 000000000..a52044f87 --- /dev/null +++ b/letsencrypt-apache/setup.py @@ -0,0 +1,59 @@ +import codecs +import os +import sys + +from setuptools import setup +from setuptools import find_packages + + +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() + + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +version = '0.6.0.dev0' + + +# This package is a simple shim around certbot-apache +install_requires = [ + 'certbot-apache', + 'letsencrypt=={0}'.format(version), +] + + +setup( + name='letsencrypt-apache', + version=version, + description="Apache plugin for Let's Encrypt", + long_description=readme, + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, +) From 910d1a94ca9751dff3d616be30ce2af651c11e09 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 10:37:40 -0700 Subject: [PATCH 234/317] letsencrypt-nginx shim --- letsencrypt-nginx/LICENSE.txt | 216 ++++++++++++++++++ letsencrypt-nginx/MANIFEST.in | 2 + letsencrypt-nginx/README.rst | 2 + .../letsencrypt_nginx/__init__.py | 8 + letsencrypt-nginx/setup.py | 59 +++++ 5 files changed, 287 insertions(+) create mode 100644 letsencrypt-nginx/LICENSE.txt create mode 100644 letsencrypt-nginx/MANIFEST.in create mode 100644 letsencrypt-nginx/README.rst create mode 100644 letsencrypt-nginx/letsencrypt_nginx/__init__.py create mode 100644 letsencrypt-nginx/setup.py diff --git a/letsencrypt-nginx/LICENSE.txt b/letsencrypt-nginx/LICENSE.txt new file mode 100644 index 000000000..02a1459be --- /dev/null +++ b/letsencrypt-nginx/LICENSE.txt @@ -0,0 +1,216 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Incorporating code from nginxparser + Copyright 2014 Fatih Erikli + Licensed MIT + + +Text of Apache License +====================== + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +Text of MIT License +=================== +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/letsencrypt-nginx/MANIFEST.in b/letsencrypt-nginx/MANIFEST.in new file mode 100644 index 000000000..97e2ad3df --- /dev/null +++ b/letsencrypt-nginx/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE.txt +include README.rst diff --git a/letsencrypt-nginx/README.rst b/letsencrypt-nginx/README.rst new file mode 100644 index 000000000..cd1f32fb8 --- /dev/null +++ b/letsencrypt-nginx/README.rst @@ -0,0 +1,2 @@ +This package is a simple shim for backwards compatibility around +``certbot-nginx``, the Nginx plugin for ``certbot``. diff --git a/letsencrypt-nginx/letsencrypt_nginx/__init__.py b/letsencrypt-nginx/letsencrypt_nginx/__init__.py new file mode 100644 index 000000000..aa14fe963 --- /dev/null +++ b/letsencrypt-nginx/letsencrypt_nginx/__init__.py @@ -0,0 +1,8 @@ +"""Let's Encrypt Nginx plugin.""" +import sys + + +import certbot_nginx + + +sys.modules['letsencrypt_nginx'] = certbot_nginx diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py new file mode 100644 index 000000000..b94b7f69f --- /dev/null +++ b/letsencrypt-nginx/setup.py @@ -0,0 +1,59 @@ +import codecs +import os +import sys + +from setuptools import setup +from setuptools import find_packages + + +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() + + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +version = '0.6.0.dev0' + + +# This package is a simple shim around certbot-nginx +install_requires = [ + 'certbot-nginx', + 'letsencrypt=={0}'.format(version), +] + + +setup( + name='letsencrypt-nginx', + version=version, + description="Nginx plugin for Let's Encrypt", + long_description=readme, + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, +) From 3ab9e91279e1b3280483af8ab93b499e1c30d1bf Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 10:46:17 -0700 Subject: [PATCH 235/317] letshelp-letsencrypt shim --- letshelp-letsencrypt/LICENSE.txt | 190 ++++++++++++++++++ letshelp-letsencrypt/MANIFEST.in | 2 + letshelp-letsencrypt/README.rst | 2 + .../letshelp_letsencrypt/__init__.py | 8 + letshelp-letsencrypt/setup.py | 61 ++++++ 5 files changed, 263 insertions(+) create mode 100644 letshelp-letsencrypt/LICENSE.txt create mode 100644 letshelp-letsencrypt/MANIFEST.in create mode 100644 letshelp-letsencrypt/README.rst create mode 100644 letshelp-letsencrypt/letshelp_letsencrypt/__init__.py create mode 100644 letshelp-letsencrypt/setup.py diff --git a/letshelp-letsencrypt/LICENSE.txt b/letshelp-letsencrypt/LICENSE.txt new file mode 100644 index 000000000..981c46c9f --- /dev/null +++ b/letshelp-letsencrypt/LICENSE.txt @@ -0,0 +1,190 @@ + Copyright 2015 Electronic Frontier Foundation and others + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/letshelp-letsencrypt/MANIFEST.in b/letshelp-letsencrypt/MANIFEST.in new file mode 100644 index 000000000..97e2ad3df --- /dev/null +++ b/letshelp-letsencrypt/MANIFEST.in @@ -0,0 +1,2 @@ +include LICENSE.txt +include README.rst diff --git a/letshelp-letsencrypt/README.rst b/letshelp-letsencrypt/README.rst new file mode 100644 index 000000000..57d0d8a3b --- /dev/null +++ b/letshelp-letsencrypt/README.rst @@ -0,0 +1,2 @@ +This package is a simple shim around the ``letshelp-certbot`` for backwards +compatibility. diff --git a/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py b/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py new file mode 100644 index 000000000..fe4e272f9 --- /dev/null +++ b/letshelp-letsencrypt/letshelp_letsencrypt/__init__.py @@ -0,0 +1,8 @@ +"""Tools for submitting server configurations.""" +import sys + + +import letshelp_certbot + + +sys.modules['letshelp_letsencrypt'] = letshelp_certbot diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py new file mode 100644 index 000000000..875c6fc92 --- /dev/null +++ b/letshelp-letsencrypt/setup.py @@ -0,0 +1,61 @@ +import codecs +import os +import sys + +from setuptools import setup +from setuptools import find_packages + + +def read_file(filename, encoding='utf8'): + """Read unicode from given file.""" + with codecs.open(filename, encoding=encoding) as fd: + return fd.read() + + +here = os.path.abspath(os.path.dirname(__file__)) +readme = read_file(os.path.join(here, 'README.rst')) + + +version = '0.6.0.dev0' + + +# This package is a simple shim around letshelp-certbot +install_requires = ['letshelp-certbot'] + + +setup( + name='letshelp-letsencrypt', + version=version, + description="Let's help Let's Encrypt client", + long_description=readme, + url='https://github.com/letsencrypt/letsencrypt', + author="Certbot Project", + author_email='client-dev@letsencrypt.org', + license='Apache License 2.0', + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Environment :: Plugins', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: Apache Software License', + 'Operating System :: POSIX :: Linux', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Security', + 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Networking', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + ], + + packages=find_packages(), + include_package_data=True, + install_requires=install_requires, + entry_points={ + 'console_scripts': [ + 'letshelp-letsencrypt-apache = letshelp_certbot.apache:main', + ], + }, +) From cdff96ddef44df100a87defb7012cede90c7ff34 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 12:40:37 -0700 Subject: [PATCH 236/317] Choose Python for better integration with boulder --- tools/venv.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/venv.sh b/tools/venv.sh index a9cac9cf1..c9d8fdb9d 100755 --- a/tools/venv.sh +++ b/tools/venv.sh @@ -1,7 +1,14 @@ #!/bin/sh -xe # Developer virtualenv setup for Certbot client -export VENV_ARGS="--python python2" +if command -v python2; then + export VENV_ARGS="--python python2" +elif command -v python2.7; then + export VENV_ARGS="--python python2.7" +else + echo "Couldn't find python2 or python2.7 in $PATH" + exit 1 +fi ./tools/_venv_common.sh \ -e acme[dev] \ From 168d46e96045fc78c674f6af20f853b28f6da229 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 12:57:28 -0700 Subject: [PATCH 237/317] Find plugins from both new and old entrypoints --- certbot/constants.py | 3 +++ certbot/plugins/disco.py | 9 +++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/certbot/constants.py b/certbot/constants.py index ef59b8769..1d4efe80e 100644 --- a/certbot/constants.py +++ b/certbot/constants.py @@ -8,6 +8,9 @@ from acme import challenges SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins" """Setuptools entry point group name for plugins.""" +OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins" +"""Plugins Setuptools entry point before rename.""" + CLI_DEFAULTS = dict( config_files=[ "/etc/letsencrypt/cli.ini", diff --git a/certbot/plugins/disco.py b/certbot/plugins/disco.py index eb3851d34..d88b871f6 100644 --- a/certbot/plugins/disco.py +++ b/certbot/plugins/disco.py @@ -1,5 +1,6 @@ """Utilities for plugins discovery and selection.""" import collections +import itertools import logging import pkg_resources @@ -164,8 +165,12 @@ class PluginsRegistry(collections.Mapping): def find_all(cls): """Find plugins using setuptools entry points.""" plugins = {} - for entry_point in pkg_resources.iter_entry_points( - constants.SETUPTOOLS_PLUGINS_ENTRY_POINT): + entry_points = itertools.chain( + pkg_resources.iter_entry_points( + constants.SETUPTOOLS_PLUGINS_ENTRY_POINT), + pkg_resources.iter_entry_points( + constants.OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT),) + for entry_point in entry_points: plugin_ep = PluginEntryPoint(entry_point) assert plugin_ep.name not in plugins, ( "PREFIX_FREE_DISTRIBUTIONS messed up") From 65503905eba1053bfa4be5cf79801ea934963735 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 13:38:53 -0700 Subject: [PATCH 238/317] Add two entry point group test --- certbot/plugins/disco_test.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/certbot/plugins/disco_test.py b/certbot/plugins/disco_test.py index 086980695..cef6ede8f 100644 --- a/certbot/plugins/disco_test.py +++ b/certbot/plugins/disco_test.py @@ -9,11 +9,16 @@ from certbot import errors from certbot import interfaces from certbot.plugins import standalone +from certbot.plugins import webroot EP_SA = pkg_resources.EntryPoint( "sa", "certbot.plugins.standalone", attrs=("Authenticator",), dist=mock.MagicMock(key="certbot")) +EP_WR = pkg_resources.EntryPoint( + "wr", "certbot.plugins.webroot", + attrs=("Authenticator",), + dist=mock.MagicMock(key="certbot")) class PluginEntryPointTest(unittest.TestCase): @@ -176,10 +181,13 @@ class PluginsRegistryTest(unittest.TestCase): def test_find_all(self): from certbot.plugins.disco import PluginsRegistry with mock.patch("certbot.plugins.disco.pkg_resources") as mock_pkg: - mock_pkg.iter_entry_points.return_value = iter([EP_SA]) + mock_pkg.iter_entry_points.side_effect = [iter([EP_SA]), + iter([EP_WR])] plugins = PluginsRegistry.find_all() self.assertTrue(plugins["sa"].plugin_cls is standalone.Authenticator) self.assertTrue(plugins["sa"].entry_point is EP_SA) + self.assertTrue(plugins["wr"].plugin_cls is webroot.Authenticator) + self.assertTrue(plugins["wr"].entry_point is EP_WR) def test_getitem(self): self.assertEqual(self.plugin_ep, self.reg["mock"]) From 8502d096711469b647a444461f0c3363e1c4d330 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 14:03:28 -0700 Subject: [PATCH 239/317] Fixes #2831 --- certbot/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/main.py b/certbot/main.py index 72f4fe66e..309889e8e 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -528,7 +528,7 @@ def obtain_cert(config, plugins, lineage=None): notify("new certificate deployed with reload of {0} server; fullchain is {1}".format( config.installer, lineage.fullchain), pause=False) elif action == "reinstall" and config.verb == "certonly": - notify("Certificate not yet due for renewal; no action taken.") + notify("Certificate not yet due for renewal; no action taken.", pause=False) _suggest_donation_if_appropriate(config, action) From 36a520ed0cbc00c3f5da33c0db21b222fed15f09 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 15 Apr 2016 15:15:43 -0700 Subject: [PATCH 240/317] Add test to prevent regressions --- certbot/tests/main_test.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 certbot/tests/main_test.py diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py new file mode 100644 index 000000000..0e8d8e30b --- /dev/null +++ b/certbot/tests/main_test.py @@ -0,0 +1,35 @@ +"""Tests for certbot.main.""" +import unittest + + +import mock + + +from certbot import cli +from certbot import configuration +from certbot.plugins import disco as plugins_disco + + +class ObtainCertTest(unittest.TestCase): + """Tests for certbot.main.obtain_cert.""" + + def _call(self, args): + plugins = plugins_disco.PluginsRegistry.find_all() + config = configuration.NamespaceConfig( + cli.prepare_and_parse_args(plugins, args)) + + from certbot import main + with mock.patch('certbot.main._init_le_client') as mock_init: + main.obtain_cert(config, plugins) + + return mock_init() # returns the client + + @mock.patch('certbot.main._auth_from_domains') + def test_no_reinstall_text_pause(self, mock_auth): + mock_auth.return_value = (mock.ANY, 'reinstall') + # This hangs if the reinstallation notification pauses + self._call('certonly --webroot -d example.com -t'.split()) + + +if __name__ == '__main__': + unittest.main() # pragma: no cover From 82ee461e348051872c753416ab65f36f766af17d Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Fri, 8 Apr 2016 02:48:45 +0200 Subject: [PATCH 241/317] Adding renewal interval along with comment to the renewal configuration file --- certbot/storage.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/certbot/storage.py b/certbot/storage.py index 4ef614a8e..9462ad545 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -78,6 +78,9 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data): if k not in relevant_data: del config["renewalparams"][k] + config["renew_before_expiry"] = constants.RENEWER_DEFAULTS["renew_before_expiry"] + config.comments["renew_before_expiry"] = "Renewal interval" + # TODO: add human-readable comments explaining other available # parameters logger.debug("Writing new config %s.", n_filename) From cafcffb86e259eaac29bb0dc88197b1b2e9df294 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Sat, 9 Apr 2016 02:20:31 +0200 Subject: [PATCH 242/317] Fixing errors --- certbot/storage.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/certbot/storage.py b/certbot/storage.py index 9462ad545..fdbdf7a0e 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -78,8 +78,10 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data): if k not in relevant_data: del config["renewalparams"][k] - config["renew_before_expiry"] = constants.RENEWER_DEFAULTS["renew_before_expiry"] - config.comments["renew_before_expiry"] = "Renewal interval" + if "renew_before_expiry" not in config["renewalparams"]: + config["renewalparams"]["renew_before_expiry"] = ( + constants.RENEWER_DEFAULTS["renew_before_expiry"]) + config["renewalparams"].comments["renew_before_expiry"] = ["Renewal interval"] # TODO: add human-readable comments explaining other available # parameters From 2646ad4262630d7e05c2ee09b3c91c77d7c48870 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 18 Apr 2016 15:08:16 -0700 Subject: [PATCH 243/317] edit contributing.rst --- docs/contributing.rst | 432 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 docs/contributing.rst diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 000000000..3225de694 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,432 @@ +=============== +Developer Guide +=============== + +.. contents:: Table of Contents + :local: + + +.. _hacking: + +Hacking +======= + +Running a local copy of the client +---------------------------------- + +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-auto-source/letsencrypt-auto --os-packages-only + ./tools/venv.sh + +Then in each shell where you're working on the client, do: + +.. code-block:: shell + + source ./venv/bin/activate + +After that, your shell will be using the virtual environment, and you run the +client by typing: + +.. code-block:: shell + + certbot + +Activating a shell in this way makes it easier to run unit tests +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 +---------------------- + +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 +`integration`_ tests, and be compliant with the :ref:`coding style +`. + +.. _github issue tracker: https://github.com/certbot/certbot/issues +.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 + +Testing +------- + +The following tools are there to help you: + +- ``tox`` starts a full set of tests. Please note that it includes + apacheconftest, which uses the system's Apache install to test config file + parsing, so it should only be run on systems that have an + experimental, non-production Apache2 install on them. ``tox -e + apacheconftest`` can be used to run those specific Apache conf tests. + +- ``tox -e py27``, ``tox -e py26`` etc, run unit tests for specific Python + versions. + +- ``tox -e cover`` checks the test coverage only. Calling the + ``./tox.cover.sh`` script directly (or even ``./tox.cover.sh $pkg1 + $pkg2 ...`` for any subpackages) might be a bit quicker, though. + +- ``tox -e lint`` checks the style of the whole project, while + ``pylint --rcfile=.pylintrc path`` will check a single file or + specific directory only. + +- For debugging, we recommend ``pip install ipdb`` and putting + ``import ipdb; ipdb.set_trace()`` statement inside the source + code. Alternatively, you can use Python's standard library `pdb`, + 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. + +However, if you prefer to run tests, you can use Vagrant, using the Vagrantfile +in Certbot's repository. To execute the tests on a Vagrant box, the only +command you are required to run is:: + + ./tests/boulder-integration.sh + +Otherwise, please follow the following instructions. + +Mac OS X users: Run ``./tests/mac-bootstrap.sh`` instead of +``boulder-start.sh`` to install dependencies, configure the +environment, and start boulder. + +Otherwise, install `Go`_ 1.5, ``libtool-ltdl``, ``mariadb-server`` and +``rabbitmq-server`` and then start Boulder_, an ACME CA server. + +If you can't get packages of Go 1.5 for your Linux system, +you can execute the following commands to install it: + +.. code-block:: shell + + wget https://storage.googleapis.com/golang/go1.5.3.linux-amd64.tar.gz -P /tmp/ + sudo tar -C /usr/local -xzf /tmp/go1.5.3.linux-amd64.tar.gz + if ! grep -Fxq "export GOROOT=/usr/local/go" ~/.profile ; then echo "export GOROOT=/usr/local/go" >> ~/.profile; fi + if ! grep -Fxq "export PATH=\\$GOROOT/bin:\\$PATH" ~/.profile ; then echo "export PATH=\\$GOROOT/bin:\\$PATH" >> ~/.profile; fi + +These commands download `Go`_ 1.5.3 to ``/tmp/``, extracts to ``/usr/local``, +and then adds the export lines required to execute ``boulder-start.sh`` to +``~/.profile`` if they were not previously added + +Make sure you execute the following command after `Go`_ finishes installing:: + + if ! grep -Fxq "export GOPATH=\\$HOME/go" ~/.profile ; then echo "export GOPATH=\\$HOME/go" >> ~/.profile; fi + +Afterwards, you'd be able to start Boulder_ using the following command:: + + ./tests/boulder-start.sh + +The script will download, compile and run the executable; please be +patient - it will take some time... Once its ready, you will see +``Server running, listening on 127.0.0.1:4000...``. Add ``/etc/hosts`` +entries pointing ``le.wtf``, ``le1.wtf``, ``le2.wtf``, ``le3.wtf`` +and ``nginx.wtf`` to 127.0.0.1. You may now run (in a separate terminal):: + + ./tests/boulder-integration.sh && echo OK || echo FAIL + +If you would like to test `certbot_nginx` plugin (highly +encouraged) make sure to install prerequisites as listed in +``certbot-nginx/tests/boulder-integration.sh`` and rerun +the integration tests suite. + +.. _Boulder: https://github.com/certbot/boulder +.. _Go: https://golang.org + + +Code components and layout +========================== + +acme + contains all protocol specific code +certbot + all client code + + +Plugin-architecture +------------------- + +Certbot has a plugin architecture to facilitate support for +different webservers, other TLS servers, and operating systems. +The interfaces available for plugins to implement are defined in +`interfaces.py`_ and `plugins/common.py`_. + +The most common kind of plugin is a "Configurator", which is likely to +implement the `~certbot.interfaces.IAuthenticator` and +`~certbot.interfaces.IInstaller` interfaces (though some +Configurators may implement just one of those). + +There are also `~certbot.interfaces.IDisplay` plugins, +which implement bindings to alternative UI libraries. + +.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py +.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34 + + +Authenticators +-------------- + +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 +individual plugins in order to satisfy domain validation (subclasses +of `~.DVChallenge`, i.e. `~.challenges.TLSSNI01`, +`~.challenges.HTTP01`, `~.challenges.DNS`) and continuity specific +challenges (subclasses of `~.ContinuityChallenge`, +i.e. `~.challenges.RecoveryToken`, `~.challenges.RecoveryContact`, +`~.challenges.ProofOfPossession`). Continuity challenges are +always handled by the `~.ContinuityAuthenticator`, while plugins are +expected to handle `~.DVChallenge` types. +Right now, we have two authenticator plugins, the `~.ApacheConfigurator` +and the `~.StandaloneAuthenticator`. The Standalone and Apache +authenticators only solve the `~.challenges.TLSSNI01` challenge currently. +(You can set which challenges your authenticator can handle through the +:meth:`~.IAuthenticator.get_chall_pref`. + +(FYI: We also have a partial implementation for a `~.DNSAuthenticator` +in a separate branch). + + +Installer +--------- + +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 +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) +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 (Such as MTA installers). + + +Installer Development +--------------------- + +There are a few existing classes that may be beneficial while +developing a new `~certbot.interfaces.IInstaller`. +Installers aimed to reconfigure UNIX servers may use Augeas for +configuration parsing and can inherit from `~.AugeasConfigurator` class +to handle much of the interface. Installers that are unable to use +Augeas may still find the `~.Reverter` class helpful in handling +configuration checkpoints and rollback. + + +Display +~~~~~~~ + +We currently offer a pythondialog and "text" mode for displays. Display +plugins implement the `~certbot.interfaces.IDisplay` +interface. + +.. _dev-plugin: + +Writing your own plugin +======================= + +Certbot client supports dynamic discovery of plugins through the +`setuptools entry points`_. This way you can, for example, create a +custom implementation of `~certbot.interfaces.IAuthenticator` or +the `~certbot.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 +============ + +Please: + +1. **Be consistent with the rest of the code**. + +2. Read `PEP 8 - Style Guide for Python Code`_. + +3. Follow the `Google Python Style Guide`_, with the exception that we + use `Sphinx-style`_ documentation:: + + def foo(arg): + """Short description. + + :param int arg: Some number. + + :returns: Argument + :rtype: int + + """ + return arg + +4. Remember to use ``pylint``. + +.. _Google Python Style Guide: + https://google-styleguide.googlecode.com/svn/trunk/pyguide.html +.. _Sphinx-style: http://sphinx-doc.org/ +.. _PEP 8 - Style Guide for Python Code: + https://www.python.org/dev/peps/pep-0008 + +Submitting a pull request +========================= + +Steps: + +1. Write your code! +2. Make sure your environment is set up properly and that you're in your + virtualenv. You can do this by running ``./tools/venv.sh``. + (this is a **very important** step) +3. Run ``./pep8.travis.sh`` to do a cursory check of your code style. + Fix any errors. +4. Run ``tox -e lint`` to check for pylint errors. Fix any errors. +5. Run ``tox`` to run the entire test suite including coverage. Fix any errors. +6. If your code touches communication with an ACME server/Boulder, you + should run the integration tests, see `integration`_. See `Known Issues`_ + for some common failures that have nothing to do with your code. +7. Submit the PR. +8. Did your tests pass on Travis? If they didn't, it might not be your fault! + See `Known Issues`_. If it's not a known issue, fix any errors. + +.. _Known Issues: + https://github.com/certbot/certbot/wiki/Known-issues + +Updating the documentation +========================== + +In order to generate the Sphinx documentation, run the following +commands: + +.. code-block:: shell + + make -C docs clean html + +This should generate documentation in the ``docs/_build/html`` +directory. + + +Other methods for running the client +==================================== + +Vagrant +------- + +If you are a Vagrant user, Certbot 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 + sudo ./venv/bin/certbot + +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. Certbot 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 certbot -f Dockerfile-dev . + +Now run tests inside the Docker image: + +.. code-block:: shell + + docker run -it certbot bash + cd src + tox -e py27 + + +.. _prerequisites: + +Notes on OS dependencies +======================== + +OS-level dependencies can be installed like so: + +.. code-block:: shell + + letsencrypt-auto-source/letsencrypt-auto --os-packages-only + +In general... + +* ``sudo`` is required as a suggested way of running privileged process +* `Python`_ 2.6/2.7 is required +* `Augeas`_ is required for the Python bindings +* ``virtualenv`` and ``pip`` are used for managing other python library + dependencies + +.. _Python: https://wiki.python.org/moin/BeginnersGuide/Download +.. _Augeas: http://augeas.net/ +.. _Virtualenv: https://virtualenv.pypa.io + + +Debian +------ + +For squeeze you will need to: + +- Use ``virtualenv --no-site-packages -p python`` instead of ``-p python2``. + + +FreeBSD +------- + +Package installation for FreeBSD uses ``pkg``, not ports. + +FreeBSD by default uses ``tcsh``. In order to activate virtualenv (see +below), you will need a compatible shell, e.g. ``pkg install bash && +bash``. From d34b5fee0dcaaaaa499aeb669eb9b7b71586cc96 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 18 Apr 2016 18:08:55 -0400 Subject: [PATCH 244/317] Fix problem with godep and vendor directories --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5d70ca799..16b5700e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ env: global: - GOPATH=/tmp/go - PATH=$GOPATH/bin:$PATH + - GO15VENDOREXPERIMENT=1 # Fixes problems with vendor directories matrix: include: From 8ceaf29fb837f70764ef2a9dd5e1a2eb30a17329 Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Sun, 17 Apr 2016 17:42:51 -0700 Subject: [PATCH 245/317] Pass a single argument to append() The other notify() calls in this block all pass the output of report(), but this one attempts to pass two arguments, which results in the stack trace described in #2822. --- certbot/renewal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/renewal.py b/certbot/renewal.py index 180499387..3682c50d5 100644 --- a/certbot/renewal.py +++ b/certbot/renewal.py @@ -287,7 +287,7 @@ def _renew_describe_results(config, renew_successes, renew_failures, if parse_failures: notify("\nAdditionally, the following renewal configuration files " "were invalid: ") - notify(parse_failures, "parsefail") + notify(report(parse_failures, "parsefail")) if config.dry_run: notify("** DRY RUN: simulating 'certbot renew' close to cert expiry") From 1ceea35669fd8e6eff5252ef6607289619f0f3c2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 19 Apr 2016 06:56:46 -0400 Subject: [PATCH 246/317] Improve obtain_cert no pause test --- certbot/tests/main_test.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py index 0e8d8e30b..66cba64a3 100644 --- a/certbot/tests/main_test.py +++ b/certbot/tests/main_test.py @@ -13,6 +13,14 @@ from certbot.plugins import disco as plugins_disco class ObtainCertTest(unittest.TestCase): """Tests for certbot.main.obtain_cert.""" + def setUp(self): + self.get_utility_patch = mock.patch( + 'certbot.main.zope.component.getUtility') + self.mock_get_utility = self.get_utility_patch.start() + + def tearDown(self): + self.get_utility_patch.stop() + def _call(self, args): plugins = plugins_disco.PluginsRegistry.find_all() config = configuration.NamespaceConfig( @@ -26,10 +34,15 @@ class ObtainCertTest(unittest.TestCase): @mock.patch('certbot.main._auth_from_domains') def test_no_reinstall_text_pause(self, mock_auth): + mock_notification = self.mock_get_utility().notification + mock_notification.side_effect = self._assert_no_pause mock_auth.return_value = (mock.ANY, 'reinstall') - # This hangs if the reinstallation notification pauses self._call('certonly --webroot -d example.com -t'.split()) + def _assert_no_pause(self, message, height=42, pause=True): + # pylint: disable=unused-argument + self.assertFalse(pause) + if __name__ == '__main__': unittest.main() # pragma: no cover From 3780d068d17b6872826d8665d69652ac5d69a1e5 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 19 Apr 2016 14:11:17 -0400 Subject: [PATCH 247/317] Fix test farm tests --- tests/letstest/multitester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/letstest/multitester.py b/tests/letstest/multitester.py index 02dfc4410..d9491939c 100644 --- a/tests/letstest/multitester.py +++ b/tests/letstest/multitester.py @@ -349,7 +349,7 @@ def test_client_process(inqueue, outqueue): print(env.host_string) try: - install_and_launch_letsencrypt(instances[ii], boulder_url, target) + install_and_launch_certbot(instances[ii], boulder_url, target) outqueue.put((ii, target, 'pass')) print("%s - %s SUCCESS"%(target['ami'], target['name'])) except: From 5b597e0e8b28b52774bd94fc55b9ff09a1994d55 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 20 Apr 2016 09:28:11 +1000 Subject: [PATCH 248/317] Rebuild letsencrypt-auto from current source --- letsencrypt-auto-source/letsencrypt-auto | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 111f2b272..1cd04e506 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -1,6 +1,6 @@ #!/bin/sh # -# Download and run the latest release version of the Let's Encrypt client. +# Download and run the latest release version of the Certbot client. # # NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING # @@ -46,7 +46,7 @@ for arg in "$@" ; do done # letsencrypt-auto needs root access to bootstrap OS dependencies, and -# letsencrypt itself needs root access for almost all modes of operation +# certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` @@ -186,7 +186,7 @@ BootstrapDebCommon() { AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" else echo "No libaugeas0 version is available that's new enough to run the" - echo "Let's Encrypt apache plugin..." + echo "Certbot apache plugin..." fi # XXX add a case for ubuntu PPAs fi @@ -426,7 +426,7 @@ Bootstrap() { elif grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon else - echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" @@ -466,7 +466,7 @@ if [ "$1" = "--le-auto-phase2" ]; then # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" # This is the flattened list of packages letsencrypt-auto installs. To generate -# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. argparse==1.4.0 \ @@ -823,7 +823,7 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run letsencrypt..." + echo "Requesting root privileges to run certbot..." echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else @@ -831,8 +831,8 @@ else # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the letsencrypt - # package). Phase 2 checks the version of the locally installed letsencrypt. + # letsencrypt-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then # If it looks like we've never bootstrapped before, bootstrap: From b597f4a284ac17a20482808b266ece29b6fecb99 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 20 Apr 2016 09:28:41 +1000 Subject: [PATCH 249/317] [letsencrypt-auto] handle network/pypi failures more gracefully --- letsencrypt-auto-source/letsencrypt-auto | 5 +++-- letsencrypt-auto-source/letsencrypt-auto.template | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 1cd04e506..81b1801cf 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -978,8 +978,9 @@ if __name__ == '__main__': UNLIKELY_EOF # --------------------------------------------------------------------------- DeterminePythonVersion - REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` - if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then + echo "WARNING: unable to check for updates." + elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 2c8e1ec4c..168dea2af 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -248,8 +248,9 @@ else UNLIKELY_EOF # --------------------------------------------------------------------------- DeterminePythonVersion - REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` - if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then + echo "WARNING: unable to check for updates." + elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more From 3c455b7e64f44deb8a47f77f8c7e70b67db3aab9 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 20 Apr 2016 10:45:30 +1000 Subject: [PATCH 250/317] letsencrypt-auto: set CERTBOT_AUTO :) --- letsencrypt-auto-source/letsencrypt-auto | 7 +++++-- letsencrypt-auto-source/letsencrypt-auto.template | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 81b1801cf..1f2dfcf85 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -50,9 +50,12 @@ done # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` +SUDO_ENV="" +export CERTBOT_AUTO="$0" if test "`id -u`" -ne "0" ; then if command -v sudo 1>/dev/null 2>&1; then SUDO=sudo + SUDO_ENV="CERTBOT_AUTO=\"$0\"" else echo \"sudo\" is not available, will use \"su\" for installation steps... # Because the parameters in `su -c` has to be a string, @@ -824,8 +827,8 @@ UNLIKELY_EOF echo "Installation succeeded." fi echo "Requesting root privileges to run certbot..." - echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" - $SUDO "$VENV_BIN/letsencrypt" "$@" + echo " " $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" + $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" else # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. # diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 168dea2af..580ee7c07 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -50,9 +50,12 @@ done # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` +SUDO_ENV="" +export CERTBOT_AUTO="$0" if test "`id -u`" -ne "0" ; then if command -v sudo 1>/dev/null 2>&1; then SUDO=sudo + SUDO_ENV="CERTBOT_AUTO=\"$0\"" else echo \"sudo\" is not available, will use \"su\" for installation steps... # Because the parameters in `su -c` has to be a string, @@ -220,8 +223,8 @@ UNLIKELY_EOF echo "Installation succeeded." fi echo "Requesting root privileges to run certbot..." - echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" - $SUDO "$VENV_BIN/letsencrypt" "$@" + echo " " $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" + $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" else # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. # From a73986576318d9fdbe95621b37caa72bbb470797 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 20 Apr 2016 10:49:02 +1000 Subject: [PATCH 251/317] Print a deprecation warning with the ancient letsencrypt-auto --- certbot/cli.py | 19 ++++++++++++++++++- certbot/main.py | 1 + 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/certbot/cli.py b/certbot/cli.py index e2c57595b..3e6f78bc1 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -37,8 +37,9 @@ helpful_parser = None # should only be used for purposes where inability to detect letsencrypt-auto # fails safely +LEAUTO = "letsencrypt-auto" fragment = os.path.join(".local", "share", "certbot") -cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "certbot" +cli_command = LEAUTO if fragment in sys.argv[0] else "certbot" # Argparse's help formatting has a lot of unhelpful peculiarities, so we want # to replace as much of it as we can... @@ -141,6 +142,22 @@ def usage_strings(plugins): return USAGE % (apache_doc, nginx_doc), SHORT_USAGE +def possible_deprecation_warning(config): + "A deprecation warning for users with the old, not-self-upgrading letsencrypt-auto." + if cli_command != LEAUTO: + return + if config.no_self_upgrade: + # users setting --no-self-upgrade might be hanging on a clent version like 0.3.0 + # or 0.5.0 which is the new script, but doesn't set CERTBOT_AUTO; they don't + # need warnings + return + if "CERTBOT_AUTO" not in os.environ: + logger.warn("You are running with an old copy of letsencrypt-auto that does " + "not receive updates, and is less reliable than more recent versions. " + "We recommend upgrading to the latest certbot-auto script, or using native " + "OS packages.") + + class _Default(object): """A class to use as a default to detect if a value is set by a user""" diff --git a/certbot/main.py b/certbot/main.py index 72f4fe66e..05347734e 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -649,6 +649,7 @@ def main(cli_args=sys.argv[1:]): args = cli.prepare_and_parse_args(plugins, cli_args) config = configuration.NamespaceConfig(args) zope.component.provideUtility(config) + cli.possible_deprecation_warning(config) # Setup logging ASAP, otherwise "No handlers could be found for # logger ..." TODO: this should be done before plugins discovery From 45681909c7fba2e0b061f6e5abe471d252c45a08 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 20 Apr 2016 14:39:26 -0400 Subject: [PATCH 252/317] Selectively rename le-auto strings --- letsencrypt-auto-source/letsencrypt-auto | 38 +++++++++---------- .../letsencrypt-auto.template | 20 +++++----- letsencrypt-auto-source/pieces/fetch.py | 2 +- .../pieces/letsencrypt-auto-requirements.txt | 2 +- letsencrypt-auto-source/tests/auto_test.py | 12 +++--- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 111f2b272..c137c4978 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -1,6 +1,6 @@ #!/bin/sh # -# Download and run the latest release version of the Let's Encrypt client. +# Download and run the latest release version of the Certbot client. # # NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING # @@ -21,7 +21,7 @@ VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="0.6.0.dev0" -# This script takes the same arguments as the main letsencrypt program, but it +# This script takes the same arguments as the main certbot program, but it # additionally responds to --verbose (more output) and --debug (allow support # for experimental platforms) for arg in "$@" ; do @@ -45,8 +45,8 @@ for arg in "$@" ; do esac done -# letsencrypt-auto needs root access to bootstrap OS dependencies, and -# letsencrypt itself needs root access for almost all modes of operation +# certbot-auto needs root access to bootstrap OS dependencies, and +# certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` @@ -186,7 +186,7 @@ BootstrapDebCommon() { AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" else echo "No libaugeas0 version is available that's new enough to run the" - echo "Let's Encrypt apache plugin..." + echo "Certbot apache plugin..." fi # XXX add a case for ubuntu PPAs fi @@ -426,7 +426,7 @@ Bootstrap() { elif grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon else - echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" @@ -446,7 +446,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then # --version output ran through grep due to python-cryptography DeprecationWarnings - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -465,8 +466,8 @@ if [ "$1" = "--le-auto-phase2" ]; then # There is no $ interpolation due to quotes on starting heredoc delimiter. # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" -# This is the flattened list of packages letsencrypt-auto installs. To generate -# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# This is the flattened list of packages certbot-auto installs. To generate +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. argparse==1.4.0 \ @@ -823,16 +824,16 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run letsencrypt..." + echo "Requesting root privileges to run certbot..." echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else - # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. + # Phase 1: Upgrade certbot-auto if neceesary, then self-invoke. # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the letsencrypt - # package). Phase 2 checks the version of the locally installed letsencrypt. + # certbot-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then # If it looks like we've never bootstrapped before, bootstrap: @@ -953,7 +954,7 @@ def verified_new_le_auto(get, tag, temp_dir): stderr=dev_null) except CalledProcessError as exc: raise ExpectedError("Couldn't verify signature of downloaded " - "letsencrypt-auto.", exc) + "certbot-auto.", exc) def main(): @@ -980,27 +981,24 @@ UNLIKELY_EOF DeterminePythonVersion REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then - echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more # dependencies (curl, etc.), for better flow control, and for the option of # future Windows compatibility. "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" - # Install new copy of letsencrypt-auto. + # Install new copy of certbot-auto. # TODO: Deal with quotes in pathnames. - echo "Replacing letsencrypt-auto..." + echo "Replacing certbot-auto..." # Clone permissions with cp. chmod and chown don't have a --reference # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: - echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" - echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" # Using mv rather than cp leaves the old file descriptor pointing to the # original copy so the shell can continue to read it unmolested. mv across # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the # cp is unlikely to fail (esp. under sudo) if the rm doesn't. - echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" # TODO: Clean up temp dir safely, even if it has quotes in its path. rm -rf "$TEMP_DIR" diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 2c8e1ec4c..67f82febf 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -21,7 +21,7 @@ VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" -# This script takes the same arguments as the main letsencrypt program, but it +# This script takes the same arguments as the main certbot program, but it # additionally responds to --verbose (more output) and --debug (allow support # for experimental platforms) for arg in "$@" ; do @@ -45,7 +45,7 @@ for arg in "$@" ; do esac done -# letsencrypt-auto needs root access to bootstrap OS dependencies, and +# certbot-auto needs root access to bootstrap OS dependencies, and # certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using @@ -177,7 +177,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then # --version output ran through grep due to python-cryptography DeprecationWarnings - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -223,11 +224,11 @@ UNLIKELY_EOF echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else - # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. + # Phase 1: Upgrade certbot-auto if neceesary, then self-invoke. # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the certbot + # certbot-auto (which is always the same as that of the certbot # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then @@ -250,27 +251,24 @@ UNLIKELY_EOF DeterminePythonVersion REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then - echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more # dependencies (curl, etc.), for better flow control, and for the option of # future Windows compatibility. "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" - # Install new copy of letsencrypt-auto. + # Install new copy of certbot-auto. # TODO: Deal with quotes in pathnames. - echo "Replacing letsencrypt-auto..." + echo "Replacing certbot-auto..." # Clone permissions with cp. chmod and chown don't have a --reference # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: - echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" - echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" # Using mv rather than cp leaves the old file descriptor pointing to the # original copy so the shell can continue to read it unmolested. mv across # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the # cp is unlikely to fail (esp. under sudo) if the rm doesn't. - echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" # TODO: Clean up temp dir safely, even if it has quotes in its path. rm -rf "$TEMP_DIR" diff --git a/letsencrypt-auto-source/pieces/fetch.py b/letsencrypt-auto-source/pieces/fetch.py index 39ff7777c..38f4aa255 100644 --- a/letsencrypt-auto-source/pieces/fetch.py +++ b/letsencrypt-auto-source/pieces/fetch.py @@ -103,7 +103,7 @@ def verified_new_le_auto(get, tag, temp_dir): stderr=dev_null) except CalledProcessError as exc: raise ExpectedError("Couldn't verify signature of downloaded " - "letsencrypt-auto.", exc) + "certbot-auto.", exc) def main(): diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index 27cfb3d43..3a1ce34f1 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -1,4 +1,4 @@ -# This is the flattened list of packages letsencrypt-auto installs. To generate +# This is the flattened list of packages certbot-auto installs. To generate # this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. diff --git a/letsencrypt-auto-source/tests/auto_test.py b/letsencrypt-auto-source/tests/auto_test.py index edb5f0c04..3b7e8731b 100644 --- a/letsencrypt-auto-source/tests/auto_test.py +++ b/letsencrypt-auto-source/tests/auto_test.py @@ -231,7 +231,7 @@ class AutoTests(TestCase): * The OpenSSL sig mismatches. For tests which get to the end, we run merely ``letsencrypt --version``. - The functioning of the rest of the letsencrypt script is covered by other + The functioning of the rest of the certbot script is covered by other test suites. """ @@ -277,7 +277,7 @@ class AutoTests(TestCase): ok_(re.match(r'letsencrypt \d+\.\d+\.\d+', err.strip().splitlines()[-1])) # Make a few assertions to test the validity of the next tests: - self.assertIn('Upgrading letsencrypt-auto ', out) + self.assertIn('Upgrading certbot-auto ', out) self.assertIn('Creating virtual environment...', out) # Now we have le-auto 99.9.9 and LE 99.9.9 installed. This @@ -286,14 +286,14 @@ class AutoTests(TestCase): # Test when neither phase-1 upgrade nor phase-2 upgrade is # needed (probably a common case): out, err = run_letsencrypt_auto() - self.assertNotIn('Upgrading letsencrypt-auto ', out) + self.assertNotIn('Upgrading certbot-auto ', out) self.assertNotIn('Creating virtual environment...', out) # Test when a phase-1 upgrade is not needed but a phase-2 # upgrade is: set_le_script_version(venv_dir, '0.0.1') out, err = run_letsencrypt_auto() - self.assertNotIn('Upgrading letsencrypt-auto ', out) + self.assertNotIn('Upgrading certbot-auto ', out) self.assertIn('Creating virtual environment...', out) def test_openssl_failure(self): @@ -312,10 +312,10 @@ class AutoTests(TestCase): except CalledProcessError as exc: eq_(exc.returncode, 1) self.assertIn("Couldn't verify signature of downloaded " - "letsencrypt-auto.", + "certbot-auto.", exc.output) else: - self.fail('Signature check on letsencrypt-auto erroneously passed.') + self.fail('Signature check on certbot-auto erroneously passed.') def test_pip_failure(self): """Make sure pip stops us if there is a hash mismatch.""" From 530033a37dec26e40ba526d58694f61fd52c6066 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 21 Apr 2016 15:16:39 -0400 Subject: [PATCH 253/317] Add CLI parsing --- letsencrypt-auto-source/letsencrypt-auto | 10 ++++++++++ letsencrypt-auto-source/letsencrypt-auto.template | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index c137c4978..1b88f1cc1 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -34,6 +34,10 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --help) + HELP=1;; + --yes) + ASSUME_YES=1;; --verbose) VERBOSE=1;; [!-]*|-*[!v]*|-) @@ -81,6 +85,12 @@ else SUDO= fi +if [ $(basename $0) = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + ExperimentalBootstrap() { # Arguments: Platform name, bootstrap function name if [ "$DEBUG" = 1 ]; then diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 67f82febf..f274418f3 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -34,6 +34,10 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --help) + HELP=1;; + --yes) + ASSUME_YES=1;; --verbose) VERBOSE=1;; [!-]*|-*[!v]*|-) @@ -81,6 +85,12 @@ else SUDO= fi +if [ $(basename $0) = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + ExperimentalBootstrap() { # Arguments: Platform name, bootstrap function name if [ "$DEBUG" = 1 ]; then From 0fa18b608150472801ccddaead63f5b3e6a96f12 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 21 Apr 2016 15:55:28 -0400 Subject: [PATCH 254/317] Add help text --- letsencrypt-auto-source/letsencrypt-auto | 22 +++++++++++++++---- .../letsencrypt-auto.template | 22 +++++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 1b88f1cc1..df4783a72 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -20,10 +20,24 @@ VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="0.6.0.dev0" +BASENAME=$(basename $0) +USAGE="Usage: $BASENAME [OPTION] +A self-updating wrapper script for the Certbot ACME client. When run, updates +to both this script and certbot will be downloaded and installed. After +ensuring you have the latest versions installed, certbot will be invoked with +all arguments you provided. + +Help for certbot itself cannot be provided until it is installed. + + --debug attempt installation on experimental platforms + --help print this help + --no-self-upgrade do not download updates for certbot or certbot-auto + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output + --yes assume yes is the answer to all prompts + +All arguments are accepted and forwarded to the Certbot client when run." -# This script takes the same arguments as the main certbot program, but it -# additionally responds to --verbose (more output) and --debug (allow support -# for experimental platforms) for arg in "$@" ; do case "$arg" in --debug) @@ -85,7 +99,7 @@ else SUDO= fi -if [ $(basename $0) = "letsencrypt-auto" ]; then +if [ $BASENAME = "letsencrypt-auto" ]; then # letsencrypt-auto does not respect --help or --yes for backwards compatibility ASSUME_YES=1 HELP=0 diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index f274418f3..a17d11fa4 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -20,10 +20,24 @@ VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" +BASENAME=$(basename $0) +USAGE="Usage: $BASENAME [OPTION] +A self-updating wrapper script for the Certbot ACME client. When run, updates +to both this script and certbot will be downloaded and installed. After +ensuring you have the latest versions installed, certbot will be invoked with +all arguments you provided. + +Help for certbot itself cannot be provided until it is installed. + + --debug attempt installation on experimental platforms + --help print this help + --no-self-upgrade do not download updates for certbot or certbot-auto + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output + --yes assume yes is the answer to all prompts + +All arguments are accepted and forwarded to the Certbot client when run." -# This script takes the same arguments as the main certbot program, but it -# additionally responds to --verbose (more output) and --debug (allow support -# for experimental platforms) for arg in "$@" ; do case "$arg" in --debug) @@ -85,7 +99,7 @@ else SUDO= fi -if [ $(basename $0) = "letsencrypt-auto" ]; then +if [ $BASENAME = "letsencrypt-auto" ]; then # letsencrypt-auto does not respect --help or --yes for backwards compatibility ASSUME_YES=1 HELP=0 From c66f0bd18e9d65a48935eaf9d0c9ef630aa0f218 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 21 Apr 2016 16:13:17 -0400 Subject: [PATCH 255/317] Make le-auto helpful --- letsencrypt-auto-source/letsencrypt-auto | 8 ++++++-- letsencrypt-auto-source/letsencrypt-auto.template | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index df4783a72..9be546584 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -21,11 +21,11 @@ VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="0.6.0.dev0" BASENAME=$(basename $0) -USAGE="Usage: $BASENAME [OPTION] +USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates to both this script and certbot will be downloaded and installed. After ensuring you have the latest versions installed, certbot will be invoked with -all arguments you provided. +all arguments you have provided. Help for certbot itself cannot be provided until it is installed. @@ -860,6 +860,10 @@ else # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi # If it looks like we've never bootstrapped before, bootstrap: Bootstrap fi diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index a17d11fa4..fe4baeafd 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -21,11 +21,11 @@ VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" BASENAME=$(basename $0) -USAGE="Usage: $BASENAME [OPTION] +USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates to both this script and certbot will be downloaded and installed. After ensuring you have the latest versions installed, certbot will be invoked with -all arguments you provided. +all arguments you have provided. Help for certbot itself cannot be provided until it is installed. @@ -256,6 +256,10 @@ else # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi # If it looks like we've never bootstrapped before, bootstrap: Bootstrap fi From 2f81a8963e3038a5156ff660f663d55c1e3295ab Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 21 Apr 2016 15:18:27 -0700 Subject: [PATCH 256/317] move github refs back to LE --- docs/api/cb_util.rst | 5 ----- docs/api/le_util.rst | 5 +++++ docs/ciphers.rst | 4 ++-- docs/contributing.rst | 12 ++++++------ docs/using.rst | 14 +++++++------- 5 files changed, 20 insertions(+), 20 deletions(-) delete mode 100644 docs/api/cb_util.rst create mode 100644 docs/api/le_util.rst diff --git a/docs/api/cb_util.rst b/docs/api/cb_util.rst deleted file mode 100644 index 066fa906c..000000000 --- a/docs/api/cb_util.rst +++ /dev/null @@ -1,5 +0,0 @@ -:mod:`certbot.cb_util` --------------------------- - -.. automodule:: certbot.cb_util - :members: diff --git a/docs/api/le_util.rst b/docs/api/le_util.rst new file mode 100644 index 000000000..c9e332745 --- /dev/null +++ b/docs/api/le_util.rst @@ -0,0 +1,5 @@ +:mod:`certbot.le_util` +-------------------------- + +.. automodule:: certbot.le_util + :members: diff --git a/docs/ciphers.rst b/docs/ciphers.rst index be6784276..a37caa0d2 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -153,7 +153,7 @@ 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/certbot/certbot/wiki/Ciphersuite-guidance +https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance Certbot users are welcome to review these authorities to better inform their own cryptographic parameter choices. We also @@ -196,7 +196,7 @@ TODO The status of this feature is tracked as part of issue #1123 in our bug tracker. -https://github.com/certbot/certbot/issues/1123 +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 diff --git a/docs/contributing.rst b/docs/contributing.rst index 3225de694..9f7a7b3c3 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -57,8 +57,8 @@ 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/certbot/certbot/issues -.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 +.. _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 ------- @@ -146,7 +146,7 @@ encouraged) make sure to install prerequisites as listed in ``certbot-nginx/tests/boulder-integration.sh`` and rerun the integration tests suite. -.. _Boulder: https://github.com/certbot/boulder +.. _Boulder: https://github.com/letsencrypt/boulder .. _Go: https://golang.org @@ -175,8 +175,8 @@ Configurators may implement just one of those). There are also `~certbot.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py -.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34 +.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/interfaces.py +.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/plugins/common.py#L34 Authenticators @@ -323,7 +323,7 @@ Steps: See `Known Issues`_. If it's not a known issue, fix any errors. .. _Known Issues: - https://github.com/certbot/certbot/wiki/Known-issues + https://github.com/letsencrypt/letsencrypt/wiki/Known-issues Updating the documentation ========================== diff --git a/docs/using.rst b/docs/using.rst index 2b16e9a27..94a4af72d 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -93,10 +93,10 @@ s3front_ Y Y Integration with Amazon CloudFront distribution of S3 buck gandi_ Y Y Integration with Gandi's hosting products and API =========== ==== ==== =============================================================== -.. _plesk: https://github.com/plesk/certbot-plesk -.. _haproxy: https://code.greenhost.net/open/certbot-haproxy -.. _s3front: https://github.com/dlapiduz/certbot-s3front -.. _gandi: https://github.com/Gandi/certbot-gandi +.. _plesk: https://github.com/plesk/letsencrypt-plesk +.. _haproxy: https://code.greenhost.net/open/letsencrypt-haproxy +.. _s3front: https://github.com/dlapiduz/letsencrypt-s3front +.. _gandi: https://github.com/Gandi/letsencrypt-gandi Future plugins for IMAP servers, SMTP servers, IRC servers, etc, are likely to be installers but not authenticators. @@ -194,7 +194,7 @@ Third-party plugins ------------------- These plugins are listed at -https://github.com/certbot/certbot/wiki/Plugins. If you're +https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If you're interested, you can also :ref:`write your own plugin `. Renewal @@ -365,7 +365,7 @@ get support on our `forums `_. If you find a bug in the software, please do report it in our `issue tracker -`_. Remember to +`_. Remember to give us as much information as possible: - copy and paste exact command line used and the output (though mind @@ -390,7 +390,7 @@ 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/certbot/certbot/wiki/Ciphersuite-guidance +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 certbot-auto_ method, which enables you to use installer plugins From d803fb9d2a8d30e2485bf9742e57e2d800501945 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 21 Apr 2016 15:42:02 -0700 Subject: [PATCH 257/317] fix /etc/ and 3p --- docs/using.rst | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 94a4af72d..07a3b6b6c 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -27,9 +27,9 @@ To install and run the client, just type... ./certbot-auto -.. hint:: During the beta phase, Certbot enforces strict rate limits on - the number of certificates issued for one domain. It is recommended to - initially use the test server via `--test-cert` until you get the desired +.. hint:: During the beta phase, the Let's Encrypt servers enforce strict rate + limits on the number of certificates issued for one domain. It is recommended + to initially use the test server via `--test-cert` until you get the desired certificates. Throughout the documentation, whenever you see references to @@ -137,14 +137,14 @@ would obtain a single certificate for all of those names, using the ``/var/www/other`` for the second two. The webroot plugin works by creating a temporary file for each of your requested -domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Certbot +domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's Encrypt validation server makes HTTP requests to validate that the DNS for each requested domain resolves to the server running certbot. An example request made to your web server would look like: :: - 66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Certbot validation server; +https://www.certbot.com)" + 66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encryptvalidation server; +https://www.letsencrypt.org)" Note that to use the webroot plugin, your server must be configured to serve files from hidden directories. If ``/.well-known`` is treated specially by @@ -272,14 +272,14 @@ 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/certbot/live/$domain``. Rather than copying, please point +``/etc/letsencrypt/live/$domain``. Rather than copying, please point your (web) server configuration directly to those files (or create -symlinks). During the renewal_, ``/etc/certbot/live`` is updated +symlinks). During the renewal_, ``/etc/letsencrypt/live`` is updated with the latest necessary files. -.. note:: ``/etc/certbot/archive`` and ``/etc/certbot/keys`` +.. note:: ``/etc/letsencrypt/archive`` and ``/etc/letsencrypt/keys`` contain all previous keys and certificates, while - ``/etc/certbot/live`` symlinks to the latest versions. + ``/etc/letsencrypt/live`` symlinks to the latest versions. The following files are available: @@ -348,9 +348,9 @@ example configuration file is shown below: By default, the following locations are searched: -- ``/etc/certbot/cli.ini`` -- ``$XDG_CONFIG_HOME/certbot/cli.ini`` (or - ``~/.config/certbot/cli.ini`` if ``$XDG_CONFIG_HOME`` is not +- ``/etc/letsencrypt/cli.ini`` +- ``$XDG_CONFIG_HOME/letsencrypt/cli.ini`` (or + ``~/.config/letsencrypt/cli.ini`` if ``$XDG_CONFIG_HOME`` is not set). .. keep it up to date with constants.py @@ -361,7 +361,7 @@ Getting help If you're having problems you can chat with us on `IRC (#certbot @ OFTC) `_ or -get support on our `forums `_. +get support on our `forums `_. If you find a bug in the software, please do report it in our `issue tracker @@ -371,7 +371,7 @@ give 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/certbot`` (though mind they +- copy and paste logs from ``/var/log/letsencrypt`` (though mind they also might contain personally identifiable information) - copy and paste ``certbot --version`` output - your operating system, including specific version @@ -403,13 +403,13 @@ to, `install Docker`_, then issue the following command: .. code-block:: shell sudo docker run -it --rm -p 443:443 -p 80:80 --name certbot \ - -v "/etc/certbot:/etc/certbot" \ - -v "/var/lib/certbot:/var/lib/certbot" \ - quay.io/certbot/certbot:latest auth + -v "/etc/letsencrypt:/etc/letsencrypt" \ + -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ + quay.io/letsencrypt/letsencrypt:latest auth and follow the instructions (note that ``auth`` command is explicitly used - no installer plugins involved). Your new cert will be available -in ``/etc/certbot/live`` on the host. +in ``/etc/letsencrypt/live`` on the host. .. _Docker: https://docker.com .. _`install Docker`: https://docs.docker.com/userguide/ @@ -420,31 +420,31 @@ Operating System Packages **FreeBSD** - * Port: ``cd /usr/ports/security/py-certbot make install clean`` - * Package: ``pkg install py27-certbot`` + * Port: ``cd /usr/ports/security/py-letsencrypt make install clean`` + * Package: ``pkg install py27-letsencrypt`` **OpenBSD** - * Port: ``cd /usr/ports/security/certbot/client && make install clean`` - * Package: ``pkg_add certbot`` + * Port: ``cd /usr/ports/security/letsencrypt/client && make install clean`` + * Package: ``pkg_add letsencrypt`` **Arch Linux** .. code-block:: shell - sudo pacman -S certbot certbot-apache + sudo pacman -S letsencrypt letsencrypt-apache **Debian** -If you run Debian Stretch or Debian Sid, you can install certbot packages. +If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. .. code-block:: shell sudo apt-get update - sudo apt-get install certbot python-certbot-apache + sudo apt-get install letsencrypt python-letsencrypt-apache If you don't want to use the Apache plugin, you can omit the -``python-certbot-apache`` package. +``python-letsencrypt-apache`` package. Packages for Debian Jessie are coming in the next few weeks. @@ -452,7 +452,7 @@ Packages for Debian Jessie are coming in the next few weeks. .. code-block:: shell - sudo dnf install certbot + sudo dnf install letsencrypt **Gentoo** @@ -461,8 +461,8 @@ want to use the Apache plugin, it has to be installed separately: .. code-block:: shell - emerge -av app-crypt/certbot - emerge -av app-crypt/certbot-apache + emerge -av app-crypt/letsencrypt + emerge -av app-crypt/letsencrypt-apache Currently, only the Apache plugin is included in Portage. However, if you want the nginx plugin, you can use Layman to add the mrueg overlay which @@ -473,7 +473,7 @@ does include the nginx plugin package: emerge -av app-portage/layman layman -S layman -a mrueg - emerge -av app-crypt/certbot-nginx + emerge -av app-crypt/letsencrypt-nginx When using the Apache plugin, you will run into a "cannot find a cert or key directive" error if you're sporting the default Gentoo ``httpd.conf``. From 2869f06109951a393134a868a02226d9f0569a1e Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 21 Apr 2016 15:56:23 -0700 Subject: [PATCH 258/317] add README.rst --- README.rst | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 050cde82b..7c7269dba 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Disclaimer ========== -The Let's Encrypt Client is **BETA SOFTWARE**. It contains plenty of bugs and +The Certbot is **BETA SOFTWARE**. It contains plenty of bugs and rough edges, and should be tested thoroughly in staging environments before use on production systems. @@ -11,10 +11,10 @@ For more information regarding the status of the project, please see https://letsencrypt.org. Be sure to checkout the `Frequently Asked Questions (FAQ) `_. -About the Let's Encrypt Client +About Certbot ============================== -The Let's Encrypt Client is a fully-featured, extensible client for the Let's +Certbot is a fully-featured, extensible client for the Let's Encrypt CA (or any other CA that speaks the `ACME `_ protocol) that can automate the tasks of obtaining certificates and @@ -24,22 +24,22 @@ systems. Installation ------------ -If ``letsencrypt`` is packaged for your Unix OS, you can install it from -there, and run it by typing ``letsencrypt``. Because not all operating -systems have packages yet, we provide a temporary solution via the -``letsencrypt-auto`` wrapper script, which obtains some dependencies -from your OS and puts others in a python virtual environment:: +If ``certbot`` (or ``letsencrypt``) is packaged for your Unix OS, you can install +it from there, and run it by typing ``certbot`` (or ``letsencrypt``). +Because not all operating systems have packages yet, we provide a temporary +solution via the ``certbot-auto`` wrapper script, which obtains some +dependencies from your OS and puts others in a python virtual environment:: user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt user@webserver:~$ cd letsencrypt - user@webserver:~/letsencrypt$ ./letsencrypt-auto --help + user@webserver:~/letsencrypt$ ./certbot-auto --help Or for full command line help, type:: - ./letsencrypt-auto --help all + ./certbot-auto --help all -``letsencrypt-auto`` updates to the latest client release automatically. And -since ``letsencrypt-auto`` is a wrapper to ``letsencrypt``, it accepts exactly +``certbot-auto`` updates to the latest client release automatically. And +since ``certbot-auto`` is a wrapper to ``certbot``, it accepts exactly the same command line flags and arguments. More details about this script and other installation methods can be found `in the User Guide `_. @@ -47,7 +47,7 @@ other installation methods can be found `in the User Guide How to run the client --------------------- -In many cases, you can just run ``letsencrypt-auto`` or ``letsencrypt``, and the +In many cases, you can just run ``certbot-auto`` or ``certbot``, and the client will guide you through the process of obtaining and installing certs interactively. @@ -56,7 +56,7 @@ For instance, if you want to obtain a cert for ``example.com``, ``www.example.com``, and ``other.example.net``, using the Apache plugin to both obtain and install the certs, you could do this:: - ./letsencrypt-auto --apache -d example.com -d www.example.com -d other.example.net + ./certbot-auto --apache -d example.com -d www.example.com -d other.example.net (The first time you run the command, it will make an account, and ask for an email and agreement to the Let's Encrypt Subscriber Agreement; you can @@ -65,7 +65,7 @@ automate those with ``--email`` and ``--agree-tos``) If you want to use a webserver that doesn't have full plugin support yet, you can still use "standalone" or "webroot" plugins to obtain a certificate:: - ./letsencrypt-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net + ./certbot-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net Understanding the client in more depth @@ -87,7 +87,7 @@ Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing Main Website: https://letsencrypt.org/ -IRC Channel: #letsencrypt on `Freenode`_ +IRC Channel: #letsencrypt on `Freenode`_ or #certbot on `OFTC`_ Community: https://community.letsencrypt.org @@ -152,7 +152,7 @@ Current Features - standalone (runs its own simple webserver to prove you control a domain) - webroot (adds files to webroot directories in order to prove control of domains and obtain certs) - - nginx/0.8.48+ (highly experimental, not included in letsencrypt-auto) + - nginx/0.8.48+ (highly experimental, not included in certbot-auto) * The private key is generated locally on your system. * Can talk to the Let's Encrypt CA or optionally to other ACME From 61203db2ebd17a27cb0f3316812bb5d72683c546 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 22 Apr 2016 13:01:32 -0400 Subject: [PATCH 259/317] Add --yes support to arch and debian bootstrappers --- letsencrypt-auto-source/letsencrypt-auto | 14 +++++++++++--- .../pieces/bootstrappers/arch_common.sh | 6 +++++- .../pieces/bootstrappers/deb_common.sh | 8 ++++++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 9be546584..d96836e64 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -175,6 +175,10 @@ BootstrapDebCommon() { augeas_pkg="libaugeas0 augeas-lenses" AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + if [ "$ASSUME_YES" = 1 ]; then + YES_FLAG="-y" + fi + AddBackportRepo() { # ARGS: BACKPORT_NAME="$1" @@ -196,7 +200,7 @@ BootstrapDebCommon() { $SUDO apt-get update fi fi - $SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg augeas_pkg= } @@ -215,7 +219,7 @@ BootstrapDebCommon() { # XXX add a case for ubuntu PPAs fi - $SUDO apt-get install -y --no-install-recommends \ + $SUDO apt-get install $YES_FLAG --no-install-recommends \ python \ python-dev \ $virtualenv \ @@ -334,8 +338,12 @@ BootstrapArchCommon() { # pacman -T exits with 127 if there are missing dependencies missing=$($SUDO pacman -T $deps) || true + if [ "$ASSUME_YES" = 1 ]; then + noconfirm="--noconfirm" + fi + if [ "$missing" ]; then - $SUDO pacman -S --needed $missing + $SUDO pacman -S --needed $missing $noconfirm fi } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh index b2fc01a14..39e2da5fe 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh @@ -21,7 +21,11 @@ BootstrapArchCommon() { # pacman -T exits with 127 if there are missing dependencies missing=$($SUDO pacman -T $deps) || true + if [ "$ASSUME_YES" = 1 ]; then + noconfirm="--noconfirm" + fi + if [ "$missing" ]; then - $SUDO pacman -S --needed $missing + $SUDO pacman -S --needed $missing $noconfirm fi } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index 57ed11399..b2b76fd55 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -34,6 +34,10 @@ BootstrapDebCommon() { augeas_pkg="libaugeas0 augeas-lenses" AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + if [ "$ASSUME_YES" = 1 ]; then + YES_FLAG="-y" + fi + AddBackportRepo() { # ARGS: BACKPORT_NAME="$1" @@ -55,7 +59,7 @@ BootstrapDebCommon() { $SUDO apt-get update fi fi - $SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg augeas_pkg= } @@ -74,7 +78,7 @@ BootstrapDebCommon() { # XXX add a case for ubuntu PPAs fi - $SUDO apt-get install -y --no-install-recommends \ + $SUDO apt-get install $YES_FLAG --no-install-recommends \ python \ python-dev \ $virtualenv \ From 40aa4dbf91349f5ca7a191d5f00f55b49f6e92af Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 22 Apr 2016 14:51:40 -0400 Subject: [PATCH 260/317] add --yes support to red hat bootstrap script --- letsencrypt-auto-source/letsencrypt-auto | 78 ++++++++++--------- .../pieces/bootstrappers/rpm_common.sh | 78 ++++++++++--------- 2 files changed, 82 insertions(+), 74 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index d96836e64..9e1dfef42 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -240,9 +240,10 @@ BootstrapDebCommon() { BootstrapRpmCommon() { # Tested with: - # - Fedora 22, 23 (x64) + # - Fedora 20, 21, 22, 23 (x64) # - Centos 7 (x64: on DigitalOcean droplet) # - CentOS 7 Minimal install in a Hyper-V VM + # - CentOS 6 (EPEL must be installed manually) if type dnf 2>/dev/null then @@ -256,47 +257,50 @@ BootstrapRpmCommon() { exit 1 fi + pkgs=" + gcc + dialog + augeas-libs + openssl + openssl-devel + libffi-devel + redhat-rpm-config + ca-certificates + " + # Some distros and older versions of current distros use a "python27" # instead of "python" naming convention. Try both conventions. - if ! $SUDO $tool install -y \ - python \ - python-devel \ - python-virtualenv \ - python-tools \ - python-pip - then - if ! $SUDO $tool install -y \ - python27 \ - python27-devel \ - python27-virtualenv \ - python27-tools \ - python27-pip - then - echo "Could not install Python dependencies. Aborting bootstrap!" - exit 1 - fi + if $SUDO $tool list python >/dev/null 2>&1; then + pkgs="$pkgs + python + python-devel + python-virtualenv + python-tools + python-pip + " + else + pkgs="$pkgs + python27 + python27-devel + python27-virtualenv + python27-tools + python27-pip + " fi - if ! $SUDO $tool install -y \ - gcc \ - dialog \ - augeas-libs \ - openssl \ - openssl-devel \ - libffi-devel \ - redhat-rpm-config \ - ca-certificates - then - echo "Could not install additional dependencies. Aborting bootstrap!" - exit 1 - fi - - if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then - if ! $SUDO $tool install -y mod_ssl - then - echo "Apache found, but mod_ssl could not be installed." - fi + pkgs="$pkgs + mod_ssl + " + fi + + if [ "$ASSUME_YES" = 1 ]; then + yes_flag="-y" + fi + + if ! $SUDO $tool install $yes_flag $pkgs; then + echo "Could not install OS dependencies. Aborting bootstrap!" + exit 1 fi } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh index 68a11a531..0f98b4bbc 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh @@ -1,8 +1,9 @@ BootstrapRpmCommon() { # Tested with: - # - Fedora 22, 23 (x64) + # - Fedora 20, 21, 22, 23 (x64) # - Centos 7 (x64: on DigitalOcean droplet) # - CentOS 7 Minimal install in a Hyper-V VM + # - CentOS 6 (EPEL must be installed manually) if type dnf 2>/dev/null then @@ -16,46 +17,49 @@ BootstrapRpmCommon() { exit 1 fi + pkgs=" + gcc + dialog + augeas-libs + openssl + openssl-devel + libffi-devel + redhat-rpm-config + ca-certificates + " + # Some distros and older versions of current distros use a "python27" # instead of "python" naming convention. Try both conventions. - if ! $SUDO $tool install -y \ - python \ - python-devel \ - python-virtualenv \ - python-tools \ - python-pip - then - if ! $SUDO $tool install -y \ - python27 \ - python27-devel \ - python27-virtualenv \ - python27-tools \ - python27-pip - then - echo "Could not install Python dependencies. Aborting bootstrap!" - exit 1 - fi + if $SUDO $tool list python >/dev/null 2>&1; then + pkgs="$pkgs + python + python-devel + python-virtualenv + python-tools + python-pip + " + else + pkgs="$pkgs + python27 + python27-devel + python27-virtualenv + python27-tools + python27-pip + " fi - if ! $SUDO $tool install -y \ - gcc \ - dialog \ - augeas-libs \ - openssl \ - openssl-devel \ - libffi-devel \ - redhat-rpm-config \ - ca-certificates - then - echo "Could not install additional dependencies. Aborting bootstrap!" - exit 1 - fi - - if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then - if ! $SUDO $tool install -y mod_ssl - then - echo "Apache found, but mod_ssl could not be installed." - fi + pkgs="$pkgs + mod_ssl + " + fi + + if [ "$ASSUME_YES" = 1 ]; then + yes_flag="-y" + fi + + if ! $SUDO $tool install $yes_flag $pkgs; then + echo "Could not install OS dependencies. Aborting bootstrap!" + exit 1 fi } From ab2319e6097beab3b05e5428b8185a6e1b330e5e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 22 Apr 2016 15:00:24 -0400 Subject: [PATCH 261/317] Respect yes with opensuse bootstrap --- letsencrypt-auto-source/letsencrypt-auto | 7 ++++++- .../pieces/bootstrappers/suse_common.sh | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 9e1dfef42..dfa39b6d1 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -307,7 +307,12 @@ BootstrapRpmCommon() { BootstrapSuseCommon() { # SLE12 don't have python-virtualenv - $SUDO zypper -nq in -l \ + if [ "$ASSUME_YES" = 1 ]; then + zypper_flags="-nq" + install_flags="-l" + fi + + $SUDO zypper $zypper_flags in $install_flags \ python \ python-devel \ python-virtualenv \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh index 46c60f992..9ac295922 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh @@ -1,7 +1,12 @@ BootstrapSuseCommon() { # SLE12 don't have python-virtualenv - $SUDO zypper -nq in -l \ + if [ "$ASSUME_YES" = 1 ]; then + zypper_flags="-nq" + install_flags="-l" + fi + + $SUDO zypper $zypper_flags in $install_flags \ python \ python-devel \ python-virtualenv \ From 23baf225a4ee9fb9382381290781f9d9c124ac20 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 22 Apr 2016 16:44:06 -0400 Subject: [PATCH 262/317] Ask before enabling backports --- letsencrypt-auto-source/letsencrypt-auto | 34 ++++++++++++------- .../pieces/bootstrappers/deb_common.sh | 34 ++++++++++++------- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index dfa39b6d1..3ca88c279 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -183,26 +183,34 @@ BootstrapDebCommon() { # ARGS: BACKPORT_NAME="$1" BACKPORT_SOURCELINE="$2" + echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME." if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then - /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." - sleep 1s - /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." - sleep 1s - /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." - sleep 1s if echo $BACKPORT_NAME | grep -q wheezy ; then - /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + echo 'Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports"' + fi + if [ "$ASSUME_YES" = 1 ]; then + add_backports=1 + else + read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response + case $response in + [yY][eE][sS]|[yY]|"") + add_backports=1;; + *) + add_backports=0;; + esac + fi + if [ "$add_backports" = 1 ]; then + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update fi - - $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" - $SUDO apt-get update fi fi - $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg - augeas_pkg= - + if [ "$add_backports" != 0 ]; then + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + fi } diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index b2b76fd55..5370aa2f2 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -42,26 +42,34 @@ BootstrapDebCommon() { # ARGS: BACKPORT_NAME="$1" BACKPORT_SOURCELINE="$2" + echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME." if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then - /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." - sleep 1s - /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." - sleep 1s - /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." - sleep 1s if echo $BACKPORT_NAME | grep -q wheezy ; then - /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + echo 'Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports"' + fi + if [ "$ASSUME_YES" = 1 ]; then + add_backports=1 + else + read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response + case $response in + [yY][eE][sS]|[yY]|"") + add_backports=1;; + *) + add_backports=0;; + esac + fi + if [ "$add_backports" = 1 ]; then + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update fi - - $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" - $SUDO apt-get update fi fi - $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg - augeas_pkg= - + if [ "$add_backports" != 0 ]; then + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + fi } From 771f5cfe49bc43e43870cc11a390c0c646353290 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Tue, 26 Apr 2016 04:20:50 +0200 Subject: [PATCH 263/317] Implementing schoen's correction --- certbot/storage.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certbot/storage.py b/certbot/storage.py index fdbdf7a0e..42cbfc2c9 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -79,9 +79,8 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data): del config["renewalparams"][k] if "renew_before_expiry" not in config["renewalparams"]: - config["renewalparams"]["renew_before_expiry"] = ( + config["renewalparams"].comments["renew_before_expiry"] = ( constants.RENEWER_DEFAULTS["renew_before_expiry"]) - config["renewalparams"].comments["renew_before_expiry"] = ["Renewal interval"] # TODO: add human-readable comments explaining other available # parameters From 070642faafd2eb5fccac04dd8f14c6b7c1d0528d Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Tue, 26 Apr 2016 05:26:07 +0200 Subject: [PATCH 264/317] Fixing renew_before_expiry comment --- certbot/storage.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/certbot/storage.py b/certbot/storage.py index 42cbfc2c9..82f07e3c5 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -79,8 +79,7 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data): del config["renewalparams"][k] if "renew_before_expiry" not in config["renewalparams"]: - config["renewalparams"].comments["renew_before_expiry"] = ( - constants.RENEWER_DEFAULTS["renew_before_expiry"]) + config.final_comment = ["renew_before_expiry = 30 days"] # TODO: add human-readable comments explaining other available # parameters From e3ad290dff710152f95b1418245dcb2247c72f38 Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Wed, 27 Apr 2016 02:50:36 +0200 Subject: [PATCH 265/317] Moving renew_before_expiry comment to initial_comment --- certbot/storage.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certbot/storage.py b/certbot/storage.py index 82f07e3c5..caf268933 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -78,8 +78,8 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data): if k not in relevant_data: del config["renewalparams"][k] - if "renew_before_expiry" not in config["renewalparams"]: - config.final_comment = ["renew_before_expiry = 30 days"] + if "renew_before_expiry" not in config: + config.initial_comment = ["renew_before_expiry = 30 days"] # TODO: add human-readable comments explaining other available # parameters From bf6f6b636cf70f964dcd7d3fed5e31d88eabce8d Mon Sep 17 00:00:00 2001 From: Amjad Mashaal Date: Thu, 28 Apr 2016 02:32:53 +0200 Subject: [PATCH 266/317] Setting value of commented renew_before_expiry to default value --- certbot/storage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certbot/storage.py b/certbot/storage.py index caf268933..c4bfb3e28 100644 --- a/certbot/storage.py +++ b/certbot/storage.py @@ -79,7 +79,8 @@ def write_renewal_config(o_filename, n_filename, target, relevant_data): del config["renewalparams"][k] if "renew_before_expiry" not in config: - config.initial_comment = ["renew_before_expiry = 30 days"] + default_interval = constants.RENEWER_DEFAULTS["renew_before_expiry"] + config.initial_comment = ["renew_before_expiry = " + default_interval] # TODO: add human-readable comments explaining other available # parameters From 528a816f704b3d359c423425eadc1c66c4d9f181 Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Mon, 2 May 2016 09:30:32 +0300 Subject: [PATCH 267/317] Don't fail authentication when vhost cannot be found Should fix #677 and #2600. --- certbot-apache/certbot_apache/tls_sni_01.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/certbot-apache/certbot_apache/tls_sni_01.py b/certbot-apache/certbot_apache/tls_sni_01.py index 1236c2eb9..a8a931fd6 100644 --- a/certbot-apache/certbot_apache/tls_sni_01.py +++ b/certbot-apache/certbot_apache/tls_sni_01.py @@ -4,6 +4,7 @@ import os import logging from certbot.plugins import common +from certbot.errors import PluginError from certbot_apache import obj from certbot_apache import parser @@ -116,12 +117,21 @@ class ApacheTlsSni01(common.TLSSNI01): def _get_addrs(self, achall): """Return the Apache addresses needed for TLS-SNI-01.""" - vhost = self.configurator.choose_vhost(achall.domain, temp=True) # TODO: Checkout _default_ rules. addrs = set() default_addr = obj.Addr(("*", str( self.configurator.config.tls_sni_01_port))) + try: + vhost = self.configurator.choose_vhost(achall.domain, temp=True) + except PluginError: + # We couldn't find the virtualhost for this domain, possibly + # because it's a new vhost that's not configured yet (GH #677), + # or perhaps because there were multiple sections + # in the config file (GH #1042). See also GH #2600. + addrs.add(default_addr) + return addrs + for addr in vhost.addrs: if "_default_" == addr.get_addr(): addrs.add(default_addr) From 8b4f48556d2f3b1614641ddd87ada36586f99e4a Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Mon, 2 May 2016 09:45:27 +0300 Subject: [PATCH 268/317] Catch the right exception Conrary to the docstring of choose_vhost(), when you run non-interactive certificate renewals and the Apache plugin fails to discover the correct vhost, it raises MissingCommandlineFlag and not PluginError. --- certbot-apache/certbot_apache/tls_sni_01.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certbot-apache/certbot_apache/tls_sni_01.py b/certbot-apache/certbot_apache/tls_sni_01.py index a8a931fd6..f14f7be0f 100644 --- a/certbot-apache/certbot_apache/tls_sni_01.py +++ b/certbot-apache/certbot_apache/tls_sni_01.py @@ -4,7 +4,7 @@ import os import logging from certbot.plugins import common -from certbot.errors import PluginError +from certbot.errors import PluginError, MissingCommandlineFlag from certbot_apache import obj from certbot_apache import parser @@ -124,7 +124,7 @@ class ApacheTlsSni01(common.TLSSNI01): try: vhost = self.configurator.choose_vhost(achall.domain, temp=True) - except PluginError: + except (PluginError, MissingCommandlineFlag): # We couldn't find the virtualhost for this domain, possibly # because it's a new vhost that's not configured yet (GH #677), # or perhaps because there were multiple sections From d73e2e68ac65a80a4407f83ebd1b2ea1fa25681e Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Mon, 2 May 2016 11:45:07 +0300 Subject: [PATCH 269/317] Add a test for #2906 --- .../certbot_apache/tests/tls_sni_01_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py index 17ef92004..aa6a2a09c 100644 --- a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py +++ b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py @@ -4,6 +4,7 @@ import shutil import mock +from certbot import errors from certbot.plugins import common_test from certbot_apache import obj @@ -137,6 +138,16 @@ class TlsSniPerformTest(util.ApacheTest): set([obj.Addr.fromstring("*:443")]), self.sni._get_addrs(self.achalls[0])) + def test_get_addrs_no_vhost_found(self): + self.sni.configurator.choose_vhost = mock.Mock( + side_effect=errors.MissingCommandlineFlag( + "Failed to run Apache plugin non-interactively")) + + # pylint: disable=protected-access + self.assertEqual( + set([obj.Addr.fromstring("*:443")]), + self.sni._get_addrs(self.achalls[0])) + if __name__ == "__main__": unittest.main() # pragma: no cover From 0f228e935dd39ef72792aa39b70a2163b665594d Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 2 May 2016 11:52:57 -0700 Subject: [PATCH 270/317] Add backports countdown when using --yes/letsencrypt-auto --- letsencrypt-auto-source/letsencrypt-auto | 6 ++++++ letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 3ca88c279..adeb94e3e 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -191,6 +191,12 @@ BootstrapDebCommon() { echo 'Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports"' fi if [ "$ASSUME_YES" = 1 ]; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s add_backports=1 else read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index 5370aa2f2..e91f52261 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -50,6 +50,12 @@ BootstrapDebCommon() { echo 'Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports"' fi if [ "$ASSUME_YES" = 1 ]; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s add_backports=1 else read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response From 8f6c289e780903ce68b3b66d71a672b866b2e47e Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 2 May 2016 13:59:42 -0700 Subject: [PATCH 271/317] incorperate jsha's comments --- README.rst | 4 ++++ docs/contributing.rst | 2 +- docs/packaging.rst | 2 +- docs/using.rst | 11 ++++++----- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 7c7269dba..a2af9ec59 100644 --- a/README.rst +++ b/README.rst @@ -21,6 +21,10 @@ protocol) that can automate the tasks of obtaining certificates and configuring webservers to use them. This client runs on Unix-based operating systems. +Until May 2016, Certbot was named simply ``letsencrypt`` or ``letsencrypt-auto``, +depending on install method. Instructions on the Internet, and some pieces of the +software, may still refer to this older name. + Installation ------------ diff --git a/docs/contributing.rst b/docs/contributing.rst index 9f7a7b3c3..60cc63e66 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -175,7 +175,7 @@ Configurators may implement just one of those). There are also `~certbot.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/interfaces.py +.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/interfaces.py .. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/plugins/common.py#L34 diff --git a/docs/packaging.rst b/docs/packaging.rst index bd366dbaa..5f09b65fa 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -3,4 +3,4 @@ Packaging Guide =============== Documentation can be found at -https://github.com/certbot/certbot/wiki/Packaging. +https://github.com/letsencrypt/letsencrypt/wiki/Packaging. diff --git a/docs/using.rst b/docs/using.rst index 07a3b6b6c..56ce78e80 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -27,7 +27,7 @@ To install and run the client, just type... ./certbot-auto -.. hint:: During the beta phase, the Let's Encrypt servers enforce strict rate +.. hint:: The Let's Encrypt servers enforce rate limits on the number of certificates issued for one domain. It is recommended to initially use the test server via `--test-cert` until you get the desired certificates. @@ -144,7 +144,7 @@ made to your web server would look like: :: - 66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encryptvalidation server; +https://www.letsencrypt.org)" + 66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)" Note that to use the webroot plugin, your server must be configured to serve files from hidden directories. If ``/.well-known`` is treated specially by @@ -360,8 +360,9 @@ Getting help ============ If you're having problems you can chat with us on `IRC (#certbot @ -OFTC) `_ or -get support on our `forums `_. +OFTC) `_ or at +`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 @@ -420,7 +421,7 @@ Operating System Packages **FreeBSD** - * Port: ``cd /usr/ports/security/py-letsencrypt make install clean`` + * Port: ``cd /usr/ports/security/py-letsencrypt && make install clean`` * Package: ``pkg install py27-letsencrypt`` **OpenBSD** From a9ecd146a9797eb068827bf6c357fde15f3a9a03 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 2 May 2016 14:16:41 -0700 Subject: [PATCH 272/317] Make certbot accept --yes flag --- certbot/cli.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/certbot/cli.py b/certbot/cli.py index e2c57595b..40af5dccb 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -649,6 +649,9 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "automation", "--no-self-upgrade", action="store_true", help="(letsencrypt-auto only) prevent the letsencrypt-auto script from" " upgrading itself to newer released versions") + helpful.add( + "automation", "--yes", action="store_true", + help="(letsencrypt-auto only) assume yes is the answer to all prompts") helpful.add( "automation", "-q", "--quiet", dest="quiet", action="store_true", help="Silence all output except errors. Useful for automation via cron." From b844b7d605b2bcaa174cf4b025af926bffd1bc81 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 3 May 2016 15:44:36 -0700 Subject: [PATCH 273/317] Create certbot-auto during release process --- tools/release.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/release.sh b/tools/release.sh index d41192af9..06aefea44 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -188,9 +188,10 @@ while ! openssl dgst -sha256 -verify $RELEASE_OPENSSL_PUBKEY -signature \ done # copy leauto to the root, overwriting the previous release version +cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto -git add letsencrypt-auto letsencrypt-auto-source +git add certbot-auto letsencrypt-auto letsencrypt-auto-source git diff --cached git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version" git tag --local-user "$RELEASE_GPG_KEY" --sign --message "Release $version" "$tag" From 891b186856026da0d11e5916bfe78280ca7faa0d Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Tue, 3 May 2016 17:04:30 -0700 Subject: [PATCH 274/317] changed wiki refs, added some words --- docs/ciphers.rst | 6 +++--- docs/contributing.rst | 14 +++++++------- docs/packaging.rst | 2 +- docs/using.rst | 10 +++++----- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/ciphers.rst b/docs/ciphers.rst index a37caa0d2..5a046e757 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -131,7 +131,7 @@ 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 +team first, before asking the Let's Encrypt project or EFF to change Certbot'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. @@ -153,7 +153,7 @@ 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 +https://github.com/certbot/certbot/wiki/Ciphersuite-guidance Certbot users are welcome to review these authorities to better inform their own cryptographic parameter choices. We also @@ -196,7 +196,7 @@ TODO The status of this feature is tracked as part of issue #1123 in our bug tracker. -https://github.com/letsencrypt/letsencrypt/issues/1123 +https://github.com/certbot/certbot/issues/1123 Prior to implementation of #1123, the client does not actually modify ciphersuites (this is intended to be implemented as a "configuration diff --git a/docs/contributing.rst b/docs/contributing.rst index 60cc63e66..49e9e146d 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -20,8 +20,8 @@ once: .. code-block:: shell - git clone https://github.com/letsencrypt/letsencrypt - cd letsencrypt + git clone https://github.com/certbot/certbot + cd certbot ./letsencrypt-auto-source/letsencrypt-auto --os-packages-only ./tools/venv.sh @@ -57,8 +57,8 @@ 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 -.. _Good Volunteer Task: https://github.com/letsencrypt/letsencrypt/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 +.. _github issue tracker: https://github.com/certbot/certbot/issues +.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 Testing ------- @@ -175,8 +175,8 @@ Configurators may implement just one of those). There are also `~certbot.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/interfaces.py -.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/letsencrypt/plugins/common.py#L34 +.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py +.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34 Authenticators @@ -323,7 +323,7 @@ Steps: See `Known Issues`_. If it's not a known issue, fix any errors. .. _Known Issues: - https://github.com/letsencrypt/letsencrypt/wiki/Known-issues + https://github.com/certbot/certbot/wiki/Known-issues Updating the documentation ========================== diff --git a/docs/packaging.rst b/docs/packaging.rst index 5f09b65fa..bd366dbaa 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -3,4 +3,4 @@ Packaging Guide =============== Documentation can be found at -https://github.com/letsencrypt/letsencrypt/wiki/Packaging. +https://github.com/certbot/certbot/wiki/Packaging. diff --git a/docs/using.rst b/docs/using.rst index 56ce78e80..0dcab4971 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -194,7 +194,7 @@ Third-party plugins ------------------- These plugins are listed at -https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If you're +https://github.com/certbot/certbot/wiki/Plugins. If you're interested, you can also :ref:`write your own plugin `. Renewal @@ -362,11 +362,11 @@ Getting help If you're having problems you can chat with us on `IRC (#certbot @ OFTC) `_ or at `IRC (#letsencrypt @ freenode) `_ -or get support on our `forums `_. +or get support on the Let's Encrypt `forums `_. If you find a bug in the software, please do report it in our `issue tracker -`_. Remember to +`_. Remember to give us as much information as possible: - copy and paste exact command line used and the output (though mind @@ -391,7 +391,7 @@ 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 +manually. https://github.com/certbot/certbot/wiki/Ciphersuite-guidance provides some information about recommended ciphersuites. If none of these make much sense to you, you should definitely use the certbot-auto_ method, which enables you to use installer plugins @@ -526,7 +526,7 @@ whole process is described in the :doc:`contributing`. Comparison of different methods ------------------------------- -Unless you have a very specific requirements, we kindly ask you to use +Unless you have a very specific requirements, we kindly suggest that you use the certbot-auto_ method. It's the fastest, the most thoroughly tested and the most reliable way of getting our software and the free SSL certificates! From e1d93663997bca079d7a415733a6fc9b11bbc803 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 4 May 2016 10:52:58 -0700 Subject: [PATCH 275/317] updated README --- CHANGES.rst | 4 ++-- README.rst | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 55e4bd796..4ce41a8bc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,9 +3,9 @@ ChangeLog Please note: the change log will only get updated after first release - for now please use the -`commit log `_. +`commit log `_. To see the changes in a given release, inspect the github milestone for the release. For instance: -https://github.com/letsencrypt/letsencrypt/issues?utf8=%E2%9C%93&q=milestone%3A0.3.0 +https://github.com/certbot/certbot/issues?utf8=%E2%9C%93&q=milestone%3A0.3.0 diff --git a/README.rst b/README.rst index a2af9ec59..60e11901c 100644 --- a/README.rst +++ b/README.rst @@ -34,9 +34,9 @@ Because not all operating systems have packages yet, we provide a temporary solution via the ``certbot-auto`` wrapper script, which obtains some dependencies from your OS and puts others in a python virtual environment:: - user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt - user@webserver:~$ cd letsencrypt - user@webserver:~/letsencrypt$ ./certbot-auto --help + user@webserver:~$ git clone https://github.com/certbot/certbot + user@webserver:~$ cd certbot + user@webserver:~/certbot$ ./certbot-auto --help Or for full command line help, type:: @@ -85,7 +85,7 @@ Links Documentation: https://letsencrypt.readthedocs.org -Software project: https://github.com/letsencrypt/letsencrypt +Software project: https://github.com/certbot/certbot Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html @@ -107,12 +107,12 @@ email to client-dev+subscribe@letsencrypt.org) -.. |build-status| image:: https://travis-ci.org/letsencrypt/letsencrypt.svg?branch=master - :target: https://travis-ci.org/letsencrypt/letsencrypt +.. |build-status| image:: https://travis-ci.org/certbot/certbot.svg?branch=master + :target: https://travis-ci.org/certbot/certbot :alt: Travis CI status -.. |coverage| image:: https://coveralls.io/repos/letsencrypt/letsencrypt/badge.svg?branch=master - :target: https://coveralls.io/r/letsencrypt/letsencrypt +.. |coverage| image:: https://coveralls.io/repos/certbot/certbot/badge.svg?branch=master + :target: https://coveralls.io/r/certbot/certbot :alt: Coverage status .. |docs| image:: https://readthedocs.org/projects/letsencrypt/badge/ @@ -159,7 +159,7 @@ Current Features - nginx/0.8.48+ (highly experimental, not included in certbot-auto) * The private key is generated locally on your system. -* Can talk to the Let's Encrypt CA or optionally to other ACME +* Can talk to the Let's Encrypt CA or optionally to other ACME compliant services. * Can get domain-validated (DV) certificates. * Can revoke certificates. @@ -174,4 +174,5 @@ Current Features .. _Freenode: https://webchat.freenode.net?channels=%23letsencrypt +.. _OFTC: https://webchat.oftc.net?channels=%23certbot .. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev From d9f36df96f458c98a81967074378118c2b7c970b Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 4 May 2016 16:02:48 -0700 Subject: [PATCH 276/317] contribute back changes from website --- docs/using.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 0dcab4971..83377ecee 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -433,7 +433,7 @@ Operating System Packages .. code-block:: shell - sudo pacman -S letsencrypt letsencrypt-apache + sudo pacman -S letsencrypt **Debian** @@ -442,10 +442,10 @@ If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. .. code-block:: shell sudo apt-get update - sudo apt-get install letsencrypt python-letsencrypt-apache + sudo apt-get install certbot python-certbot-apache If you don't want to use the Apache plugin, you can omit the -``python-letsencrypt-apache`` package. +``python-certbot-apache`` package. Packages for Debian Jessie are coming in the next few weeks. @@ -466,8 +466,12 @@ want to use the Apache plugin, it has to be installed separately: emerge -av app-crypt/letsencrypt-apache Currently, only the Apache plugin is included in Portage. However, if you -want the nginx plugin, you can use Layman to add the mrueg overlay which -does include the nginx plugin package: +Warning! +You can use Layman to add the mrueg overlay which does include a package for the +Certbot Nginx plugin, however, this plugin is known to be buggy and should only +be used with caution after creating a backup up your Nginx configuration. +We strongly recommend you use the app-crypt/letsencrypt package instead until +the Nginx plugin is ready. .. code-block:: shell From 144f28690b75e0c4deef9395d774f8b0e774c28a Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Wed, 4 May 2016 17:03:52 -0700 Subject: [PATCH 277/317] added new docs links --- CONTRIBUTING.md | 2 +- README.rst | 14 ++++++++++---- certbot-apache/docs/conf.py | 2 +- certbot-compatibility-test/docs/conf.py | 2 +- certbot-nginx/docs/conf.py | 2 +- letshelp-certbot/docs/conf.py | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bf19b18e1..5f1625658 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://certbot.eff.org/docs/contributing.html diff --git a/README.rst b/README.rst index 60e11901c..1bfb8fdab 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,12 @@ Until May 2016, Certbot was named simply ``letsencrypt`` or ``letsencrypt-auto`` depending on install method. Instructions on the Internet, and some pieces of the software, may still refer to this older name. +Contributing +------------ + +If you'd like to contribute to this project please read `Developer Guide +`_. + Installation ------------ @@ -46,7 +52,7 @@ Or for full command line help, type:: since ``certbot-auto`` is a wrapper to ``certbot``, it accepts exactly the same command line flags and arguments. More details about this script and other installation methods can be found `in the User Guide -`_. +`_. How to run the client --------------------- @@ -77,17 +83,17 @@ Understanding the client in more depth To understand what the client is doing in detail, it's important to understand the way it uses plugins. Please see the `explanation of -plugins `_ in +plugins `_ in the User Guide. Links ===== -Documentation: https://letsencrypt.readthedocs.org +Documentation: https://certbot.eff.org/docs Software project: https://github.com/certbot/certbot -Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html +Notes for developers: https://certbot.eff.org/docs/contributing.html Main Website: https://letsencrypt.org/ diff --git a/certbot-apache/docs/conf.py b/certbot-apache/docs/conf.py index 2f996c7f4..d2fe15581 100644 --- a/certbot-apache/docs/conf.py +++ b/certbot-apache/docs/conf.py @@ -314,5 +314,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), } diff --git a/certbot-compatibility-test/docs/conf.py b/certbot-compatibility-test/docs/conf.py index 1ef69ab2d..f89f4b368 100644 --- a/certbot-compatibility-test/docs/conf.py +++ b/certbot-compatibility-test/docs/conf.py @@ -311,7 +311,7 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), 'certbot-apache': ( 'https://letsencrypt-apache.readthedocs.org/en/latest/', None), 'certbot-nginx': ( diff --git a/certbot-nginx/docs/conf.py b/certbot-nginx/docs/conf.py index fa00e6503..167abb4fb 100644 --- a/certbot-nginx/docs/conf.py +++ b/certbot-nginx/docs/conf.py @@ -307,5 +307,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), } diff --git a/letshelp-certbot/docs/conf.py b/letshelp-certbot/docs/conf.py index 905d70662..17d8b3ea9 100644 --- a/letshelp-certbot/docs/conf.py +++ b/letshelp-certbot/docs/conf.py @@ -307,5 +307,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), } From 4e19f9eae0c0cdf6def576bd98949fee12e23b7c Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 4 May 2016 17:28:16 -0700 Subject: [PATCH 278/317] venv is still named letsencrypt --- certbot/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/cli.py b/certbot/cli.py index e2c57595b..97b1a5399 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -37,7 +37,7 @@ helpful_parser = None # should only be used for purposes where inability to detect letsencrypt-auto # fails safely -fragment = os.path.join(".local", "share", "certbot") +fragment = os.path.join(".local", "share", "letsencrypt") cli_command = "letsencrypt-auto" if fragment in sys.argv[0] else "certbot" # Argparse's help formatting has a lot of unhelpful peculiarities, so we want From d38cf4a74ed565679c1cd9998c23b8af55be9bfe Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 4 May 2016 17:55:12 -0700 Subject: [PATCH 279/317] Build shim packages in next release --- tools/release.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/release.sh b/tools/release.sh index 06aefea44..e6169c5c8 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -45,7 +45,7 @@ export GPG_TTY=$(tty) PORT=${PORT:-1234} # subpackages to be released -SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot"} +SUBPKGS=${SUBPKGS:-"acme certbot-apache certbot-nginx letshelp-certbot letsencrypt letsencrypt-apache letsencrypt-nginx letshelp-letsencrypt"} subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)" # certbot_compatibility_test is not packaged because: # - it is not meant to be used by anyone else than Certbot devs @@ -162,12 +162,12 @@ done deactivate # pin pip hashes of the things we just built -for pkg in acme certbot certbot-apache ; do +for pkg in acme certbot certbot-apache letsencrypt letsencrypt-apache ; do echo $pkg==$version \\ pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),' done > /tmp/hashes.$$ -if ! wc -l /tmp/hashes.$$ | grep -qE "^\s*9 " ; then +if ! wc -l /tmp/hashes.$$ | grep -qE "^\s*15 " ; then echo Unexpected pip hash output exit 1 fi From f3172bcfeed8666c9904bdabfcfbb28fd17c947a Mon Sep 17 00:00:00 2001 From: Jeremy Gillula Date: Thu, 5 May 2016 08:55:49 -0700 Subject: [PATCH 280/317] Changing some "will happen"s to "hopefully will happen"s --- README.rst | 4 ++-- docs/using.rst | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 050cde82b..cc4e53bda 100644 --- a/README.rst +++ b/README.rst @@ -128,8 +128,8 @@ System Requirements =================== The Let's Encrypt Client presently only runs on Unix-ish OSes that include -Python 2.6 or 2.7; Python 3.x support will be added after the Public Beta -launch. The client requires root access in order to write to +Python 2.6 or 2.7; Python 3.x support will hopefully be added after the Public +Beta launch. The client requires root access in order to write to ``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and modify webserver configurations (if you use the ``apache`` or ``nginx`` diff --git a/docs/using.rst b/docs/using.rst index 66c5907ae..8f56554ce 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -124,7 +124,7 @@ or ``--webroot-path /usr/share/nginx/html`` are two common webroot paths. If you're getting a certificate for many domains at once, the plugin needs to know where each domain's files are served from, which could -potentially be a separate directory for each domain. When requested a +potentially be a separate directory for each domain. When requesting a certificate for multiple domains, each domain will use the most recently specified ``--webroot-path``. So, for instance, @@ -184,11 +184,11 @@ be on a different computer. Nginx ----- -In the future, if you're running Nginx you can use this plugin to -automatically obtain and install your certificate. The Nginx plugin -is still experimental, however, and is not installed with -letsencrypt-auto_. If installed, you can select this plugin on the -command line by including ``--nginx``. +In the future, if you're running Nginx you will hopefully be able to use this +plugin to automatically obtain and install your certificate. The Nginx plugin is +still experimental, however, and is not installed with letsencrypt-auto_. If +installed, you can select this plugin on the command line by including +``--nginx``. Third-party plugins ------------------- @@ -446,7 +446,7 @@ If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. If you don't want to use the Apache plugin, you can omit the ``python-letsencrypt-apache`` package. -Packages for Debian Jessie are coming in the next few weeks. +Packages for Debian Jessie will hopefully be coming in the next few weeks. **Fedora** From 127ba71c43770d233d1604ab8a2b32e574c12e8b Mon Sep 17 00:00:00 2001 From: Jeremy Gillula Date: Thu, 5 May 2016 11:17:47 -0700 Subject: [PATCH 281/317] Adding the fact that we actually have backports for Debian Jessie to the docs --- docs/using.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 8f56554ce..60c074d75 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -446,7 +446,13 @@ If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. If you don't want to use the Apache plugin, you can omit the ``python-letsencrypt-apache`` package. -Packages for Debian Jessie will hopefully be coming in the next few weeks. +Packages exist for Debian Jessie via backports. First you'll have to follow the +instructions at http://backports.debian.org/Instructions/ to enable the Jessie backports +repo, if you have not already done so. Then run: + +.. code-block:: shell + + sudo apt-get install certbot python-certbot-apache -t jessie-backports **Fedora** From fbbbb5b51634d348f82f893e1a02e98c0fcf3606 Mon Sep 17 00:00:00 2001 From: Jeremy Gillula Date: Thu, 5 May 2016 11:31:28 -0700 Subject: [PATCH 282/317] Turns out the public beta is over, but still no Python 3.0 support. We over-promised! --- README.rst | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index cc4e53bda..236bdf8f4 100644 --- a/README.rst +++ b/README.rst @@ -128,16 +128,15 @@ System Requirements =================== The Let's Encrypt Client presently only runs on Unix-ish OSes that include -Python 2.6 or 2.7; Python 3.x support will hopefully be added after the Public -Beta launch. The client requires root access in order to write to -``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to -bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and -modify webserver configurations (if you use the ``apache`` or ``nginx`` -plugins). If none of these apply to you, it is theoretically possible to run -without root privileges, but for most users who want to avoid running an ACME -client as root, either `letsencrypt-nosudo -`_ or `simp_le -`_ are more appropriate choices. +Python 2.6 or 2.7; Python 3.x support will hopefully be added in the future. The +client requires root access in order to write to ``/etc/letsencrypt``, +``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to bind to ports 80 and 443 +(if you use the ``standalone`` plugin) and to read and modify webserver +configurations (if you use the ``apache`` or ``nginx`` plugins). If none of +these apply to you, it is theoretically possible to run without root privileges, +but for most users who want to avoid running an ACME client as root, either +`letsencrypt-nosudo `_ or +`simp_le `_ are more appropriate choices. The Apache plugin currently requires a Debian-based OS with augeas version 1.0; this includes Ubuntu 12.04+ and Debian 7+. From a65fca486cf65e88cdc8c8881c3e9f0c20db76ed Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 11:57:12 -0700 Subject: [PATCH 283/317] Specify minimum parsedatetime version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 67cefdc48..4ee56576b 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ install_requires = [ 'ConfigArgParse>=0.9.3', 'configobj', 'cryptography>=0.7', # load_pem_x509_certificate - 'parsedatetime', + 'parsedatetime>=1.3', # Calendar.parseDT 'psutil>=2.1.0', # net_connections introduced in 2.1.0 'PyOpenSSL', 'pyrfc3339', From 495371a3b8d150f989d92820e3d30abbe26ac96a Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 12:33:52 -0700 Subject: [PATCH 284/317] Use --force-reinstall to fix bad virtualenv package --- tools/_venv_common.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/_venv_common.sh b/tools/_venv_common.sh index a121af82d..dc6ca3dd2 100755 --- a/tools/_venv_common.sh +++ b/tools/_venv_common.sh @@ -18,7 +18,8 @@ virtualenv --no-site-packages $VENV_NAME $VENV_ARGS # Separately install setuptools and pip to make sure following # invocations use latest pip install -U setuptools -pip install -U pip +# --force-reinstall used to fix broken pip installation on some systems +pip install --force-reinstall -U pip pip install "$@" set +x From 785010fe5001a3c1472bf3ef47e99fb1da32f802 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Fri, 6 May 2016 12:45:51 -0700 Subject: [PATCH 285/317] Welcome to Certbot! --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 236bdf8f4..91a3cfcb5 100644 --- a/README.rst +++ b/README.rst @@ -3,9 +3,9 @@ Disclaimer ========== -The Let's Encrypt Client is **BETA SOFTWARE**. It contains plenty of bugs and -rough edges, and should be tested thoroughly in staging environments before use -on production systems. +Certbot (previously, the Let's Encrypt client) is **BETA SOFTWARE**. It +contains plenty of bugs and rough edges, and should be tested thoroughly in +staging environments before use on production systems. For more information regarding the status of the project, please see https://letsencrypt.org. Be sure to checkout the From 7167a9e108c62a865a0a6d1df2084e404d3cd2ef Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 16:41:23 -0700 Subject: [PATCH 286/317] fixes #2927 --- certbot/plugins/standalone.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/certbot/plugins/standalone.py b/certbot/plugins/standalone.py index a3bb1d8f0..16e9dc11b 100644 --- a/certbot/plugins/standalone.py +++ b/certbot/plugins/standalone.py @@ -120,6 +120,13 @@ def supported_challenges_validator(data): """ challs = data.split(",") + + # tls-sni-01 was dvsni during private beta + if "dvsni" in challs: + challs = [challenges.TLSSNI01.typ if chall == "dvsni" else chall + for chall in challs] + data = ",".join(challs) + unrecognized = [name for name in challs if name not in challenges.Challenge.TYPES] if unrecognized: From 56d7a97e6aeec9b91d3517f0afa4f0da815983c9 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 16:44:31 -0700 Subject: [PATCH 287/317] Test dvsni with standalone-supported-challenges --- certbot/plugins/standalone_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/certbot/plugins/standalone_test.py b/certbot/plugins/standalone_test.py index 9f5b14591..eb6631732 100644 --- a/certbot/plugins/standalone_test.py +++ b/certbot/plugins/standalone_test.py @@ -85,6 +85,11 @@ class SupportedChallengesValidatorTest(unittest.TestCase): def test_not_subset(self): self.assertRaises(argparse.ArgumentTypeError, self._call, "dns") + def test_dvsni(self): + self.assertEqual("tls-sni-01", self._call("dvsni")) + self.assertEqual("http-01,tls-sni-01", self._call("http-01,dvsni")) + self.assertEqual("tls-sni-01,http-01", self._call("dvsni,http-01")) + class AuthenticatorTest(unittest.TestCase): """Tests for certbot.plugins.standalone.Authenticator.""" From 30ae348a8c462a770332046f31c13a8f2cb4b183 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 16:53:33 -0700 Subject: [PATCH 288/317] Add logger message --- certbot/plugins/standalone.py | 1 + 1 file changed, 1 insertion(+) diff --git a/certbot/plugins/standalone.py b/certbot/plugins/standalone.py index 16e9dc11b..8e1cb72a4 100644 --- a/certbot/plugins/standalone.py +++ b/certbot/plugins/standalone.py @@ -123,6 +123,7 @@ def supported_challenges_validator(data): # tls-sni-01 was dvsni during private beta if "dvsni" in challs: + logger.info("Updating legacy standalone_supported_challenges value") challs = [challenges.TLSSNI01.typ if chall == "dvsni" else chall for chall in challs] data = ",".join(challs) From 4627971dc68f1cf834eb1e836b8b4ad40e39fb0f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 17:30:18 -0700 Subject: [PATCH 289/317] s/--letsencrypt/--certbot --- tests/travis-integration.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/travis-integration.sh b/tests/travis-integration.sh index 1b51f0980..c22c346b1 100755 --- a/tests/travis-integration.sh +++ b/tests/travis-integration.sh @@ -12,8 +12,8 @@ cd $GOPATH/src/github.com/letsencrypt/boulder/ # boulder's integration-test.py has code that knows to start and wait for the # boulder processes to start reliably and then will run the certbot -# boulder-interation.sh on its own. The --letsencrypt flag says to run only the +# boulder-interation.sh on its own. The --certbot flag says to run only the # certbot tests (instead of any other client tests it might run). We're # going to want to define a more robust interaction point between the boulder # and certbot tests, but that will be better built off of this. -python test/integration-test.py --letsencrypt +python test/integration-test.py --certbot From 5c0eabcd76fc73e0fd6240cb62466336ad555593 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 6 May 2016 17:42:25 -0700 Subject: [PATCH 290/317] Rename LETSENCRYPT_PATH to CERTBOT_PATH --- tests/travis-integration.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/travis-integration.sh b/tests/travis-integration.sh index c22c346b1..159a2ef80 100755 --- a/tests/travis-integration.sh +++ b/tests/travis-integration.sh @@ -6,7 +6,7 @@ set -o errexit source .tox/$TOXENV/bin/activate -export LETSENCRYPT_PATH=`pwd` +export CERTBOT_PATH=`pwd` cd $GOPATH/src/github.com/letsencrypt/boulder/ From 9059a4966408fe8a495ea79539a8619728597ddc Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Sat, 7 May 2016 18:49:18 +0100 Subject: [PATCH 291/317] Merge Augeas fix for empty section continuations From https://github.com/hercules-team/augeas/commit/568be1bc392bab6089c027de990b857ce962cc26 Fixes #2731 --- .../certbot_apache/augeas_lens/httpd.aug | 4 +- .../section-empty-continuations-2731.conf | 247 ++++++++++++++++++ 2 files changed, 249 insertions(+), 2 deletions(-) create mode 100644 certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-empty-continuations-2731.conf diff --git a/certbot-apache/certbot_apache/augeas_lens/httpd.aug b/certbot-apache/certbot_apache/augeas_lens/httpd.aug index 697d5de89..07974b364 100644 --- a/certbot-apache/certbot_apache/augeas_lens/httpd.aug +++ b/certbot-apache/certbot_apache/augeas_lens/httpd.aug @@ -45,8 +45,8 @@ autoload xfm let dels (s:string) = del s s (* deal with continuation lines *) -let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)/ " " -let sep_osp = del /([ \t]*|[ \t]*\\\\\r?\n[ \t]*)/ "" +let sep_spc = del /([ \t]+|[ \t]*\\\\\r?\n[ \t]*)+/ " " +let sep_osp = del /([ \t]*|[ \t]*\\\\\r?\n[ \t]*)*/ "" let sep_eq = del /[ \t]*=[ \t]*/ "=" let nmtoken = /[a-zA-Z:_][a-zA-Z0-9:_.-]*/ diff --git a/certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-empty-continuations-2731.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-empty-continuations-2731.conf new file mode 100644 index 000000000..3f2f96965 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/section-empty-continuations-2731.conf @@ -0,0 +1,247 @@ +#ATTENTION! +# +#DO NOT MODIFY THIS FILE BECAUSE IT WAS GENERATED AUTOMATICALLY, +#SO ALL YOUR CHANGES WILL BE LOST THE NEXT TIME THE FILE IS GENERATED. + +NameVirtualHost 192.168.100.218:80 +NameVirtualHost 10.128.178.192:80 + +NameVirtualHost 192.168.100.218:443 +NameVirtualHost 10.128.178.192:443 + + +ServerName "254020-web1.example.com" +ServerAdmin "name@example.com" + +DocumentRoot "/tmp" + + + LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" plesklog + + + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" plesklog + + + TraceEnable off + +ServerTokens ProductOnly + + + AllowOverride "All" + Options SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + +php_admin_flag engine off + + + +php_admin_flag engine off + + + + + + AllowOverride All + Options SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + php_admin_flag engine off + + + php_admin_flag engine off + + + + + Header add X-Powered-By PleskLin + + + + JkWorkersFile "/etc/httpd/conf/workers.properties" + JkLogFile /var/log/httpd/mod_jk.log + JkLogLevel info + + +#Include "/etc/httpd/conf/plesk.conf.d/ip_default/*.conf" + + + + ServerName "default" + UseCanonicalName Off + DocumentRoot "/tmp" + ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin" + + + + SSLEngine off + + + + AllowOverride None + Options None + Order allow,deny + Allow from all + + + + + +php_admin_flag engine on + + + +php_admin_flag engine on + + + + + + + + + + ServerName "default-192_168_100_218" + UseCanonicalName Off + DocumentRoot "/tmp" + ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin" + + + SSLEngine on + SSLVerifyClient none + #SSLCertificateFile "/usr/local/psa/var/certificates/cert-9MgutN" + + #SSLCACertificateFile "/usr/local/psa/var/certificates/cert-s6Wx3P" + + + AllowOverride None + Options None + Order allow,deny + Allow from all + + + + + +php_admin_flag engine on + + + +php_admin_flag engine on + + + + + + + ServerName "default-10_128_178_192" + UseCanonicalName Off + DocumentRoot "/tmp" + ScriptAlias /cgi-bin/ "/var/www/vhosts/default/cgi-bin" + + + SSLEngine on + SSLVerifyClient none + #SSLCertificateFile "/usr/local/psa/var/certificates/certxfb6025" + + + + AllowOverride None + Options None + Order allow,deny + Allow from all + + + + + +php_admin_flag engine on + + + +php_admin_flag engine on + + + + + + + + + + + DocumentRoot "/tmp" + ServerName lists + ServerAlias lists.* + UseCanonicalName Off + + ScriptAlias "/mailman/" "/usr/lib/mailman/cgi-bin/" + + Alias "/icons/" "/var/www/icons/" + Alias "/pipermail/" "/var/lib/mailman/archives/public/" + + + SSLEngine off + + + + + Options FollowSymLinks + Order allow,deny + Allow from all + + + + + + + DocumentRoot "/tmp" + ServerName lists + ServerAlias lists.* + UseCanonicalName Off + + ScriptAlias "/mailman/" "/usr/lib/mailman/cgi-bin/" + + Alias "/icons/" "/var/www/icons/" + Alias "/pipermail/" "/var/lib/mailman/archives/public/" + + SSLEngine on + SSLVerifyClient none + #SSLCertificateFile "/usr/local/psa/var/certificates/certxfb6025" + + + + Options FollowSymLinks + Order allow,deny + Allow from all + + + + + + + RPAFproxy_ips 192.168.100.218 10.128.178.192 + + + RPAFproxy_ips 192.168.100.218 10.128.178.192 + From 3d90fb809761deba0ffa18c615c0c5ac00887ba1 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Sat, 7 May 2016 21:55:31 +0100 Subject: [PATCH 292/317] Merge Augeas fix for escaped spaces in arguments From https://github.com/hercules-team/augeas/commit/f741b8b4f23dd9372dcdea18383ef9138f9c161e Fixes #2735 --- certbot-apache/certbot_apache/augeas_lens/httpd.aug | 4 ++-- .../passing/escaped-space-arguments-2735.conf | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 certbot-apache/certbot_apache/tests/apache-conf-files/passing/escaped-space-arguments-2735.conf diff --git a/certbot-apache/certbot_apache/augeas_lens/httpd.aug b/certbot-apache/certbot_apache/augeas_lens/httpd.aug index 697d5de89..f27798efe 100644 --- a/certbot-apache/certbot_apache/augeas_lens/httpd.aug +++ b/certbot-apache/certbot_apache/augeas_lens/httpd.aug @@ -58,8 +58,8 @@ let empty = Util.empty_dos let indent = Util.indent (* borrowed from shellvars.aug *) -let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'/ -let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'/ +let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ \t\r\n])|\\\\"|\\\\'|\\\\ / +let char_arg_sec = /([^\\ '"\t\r\n>]|[^ '"\t\r\n>]+[^\\ \t\r\n>])|\\\\"|\\\\'|\\\\ / let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/ let cdot = /\\\\./ diff --git a/certbot-apache/certbot_apache/tests/apache-conf-files/passing/escaped-space-arguments-2735.conf b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/escaped-space-arguments-2735.conf new file mode 100644 index 000000000..1ea53dfab --- /dev/null +++ b/certbot-apache/certbot_apache/tests/apache-conf-files/passing/escaped-space-arguments-2735.conf @@ -0,0 +1,2 @@ +RewriteCond %{HTTP:Content-Disposition} \.php [NC] +RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /.+/trackback/?\ HTTP/ [NC] From 653c7b6327508a3dba2d8a2ef7c5c4463647d7fe Mon Sep 17 00:00:00 2001 From: Michal Moravec Date: Sun, 8 May 2016 16:16:54 +0200 Subject: [PATCH 293/317] Ensure /usr/local/lib/ exists before creating libaugeas.dylib symlink in mac.sh bootstraper --- letsencrypt-auto-source/pieces/bootstrappers/mac.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index 79e58eb3f..e41db04b1 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -26,7 +26,8 @@ BootstrapMac() { # Workaround for _dlopen not finding augeas on OS X if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then echo "Applying augeas workaround" - $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib + $SUDO mkdir -p /usr/local/lib/ + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ fi if ! hash pip 2>/dev/null; then From 3c413c28b8e6ce0208289ccd12fdc8252c1ce805 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 9 May 2016 12:31:39 -0700 Subject: [PATCH 294/317] fix grammar --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 1bfb8fdab..a0bd4059b 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,7 @@ Disclaimer ========== -The Certbot is **BETA SOFTWARE**. It contains plenty of bugs and +Certbot is **BETA SOFTWARE**. It contains plenty of bugs and rough edges, and should be tested thoroughly in staging environments before use on production systems. From 555513940c8e541923cb2e28a56d62b684559dd3 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 May 2016 14:57:01 -0700 Subject: [PATCH 295/317] Explain *-hook and -q in renewal documentation --- docs/using.rst | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 60c074d75..7d7b4fbfd 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -215,12 +215,25 @@ expire in less than 30 days. The same plugin and options that were used at the time the certificate was originally issued will be used for the renewal attempt, unless you specify other plugins or options. +You can also specify hooks to be run before or after a certificate is +renewed. For example, if you want to use the standalone_ plugin to renew +your certificates, you may want to use a command like + +``letsencrypt renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"`` + +This will stop Nginx so standalone can bind to the necessary ports and +then restart Nginx after the plugin is finished. The hooks will only be +run if a certificate is due for renewal, so you can run this command +frequently without unnecessarily stopping your webserver. More +information about renewal hooks can be found by running +``letsencrypt --help renew``. + If you're sure that this command executes successfully without human intervention, you can add the command to ``crontab`` (since certificates are only renewed when they're determined to be near expiry, the command -can run on a regular basis, like every week or every day); note that -the current version provides detailed output describing either renewal -success or failure. +can run on a regular basis, like every week or every day). You may also +want to use the ``-q`` or ``--quiet`` quiet flag to silence all output +except errors. The ``--force-renew`` flag may be helpful for automating renewal; it causes the expiration time of the certificate(s) to be ignored when From 9ddabc3e9a2fcc89dd7d2f892e6a726cdb070325 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 9 May 2016 15:17:14 -0700 Subject: [PATCH 296/317] fix docs conf --- docs/conf.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fb2bdea73..b2b31ac5d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -64,8 +64,8 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'Let\'s Encrypt' -copyright = u'2014-2015, Let\'s Encrypt Project' +project = u'Certbot' +copyright = u'2014-2016, Certbot 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 @@ -225,7 +225,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'LetsEncryptdoc' +htmlhelp_basename = 'Certbotdoc' # -- Options for LaTeX output --------------------------------------------- @@ -247,8 +247,8 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'LetsEncrypt.tex', u'Let\'s Encrypt Documentation', - u'Let\'s Encrypt Project', 'manual'), + ('index', 'Certbot.tex', u'Certbot Documentation', + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -277,7 +277,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'certbot', u'Let\'s Encrypt Documentation', + ('index', 'certbot', u'Certbot Documentation', [project], 7), ('man/certbot', 'certbot', u'certbot script documentation', [project], 1), @@ -293,8 +293,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'LetsEncrypt', u'Let\'s Encrypt Documentation', - u'Let\'s Encrypt Project', 'LetsEncrypt', 'One line description of project.', + ('index', 'Certbot', u'Certbot Documentation', + u'Certbot Project', 'Certbot', 'One line description of project.', 'Miscellaneous'), ] From bbcde8cec1096ef44825862a66546d565d7419d6 Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Mon, 9 May 2016 15:27:17 -0700 Subject: [PATCH 297/317] seth changes --- docs/ciphers.rst | 4 ++-- docs/using.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ciphers.rst b/docs/ciphers.rst index 5a046e757..8996dd9ef 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -131,7 +131,7 @@ 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 or EFF to change +team first, before asking the Certbot developers to change Certbot'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. @@ -187,7 +187,7 @@ to enable updating ciphers with each new Certbot release, or certbot --update-ciphers off to disable automatic configuration updates. These features have not yet -been implemented and this syntax may change then they are implemented. +been implemented and this syntax may change when they are implemented. TODO diff --git a/docs/using.rst b/docs/using.rst index 10d4aa544..f215b335b 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -539,7 +539,7 @@ Comparison of different methods Unless you have a very specific requirements, we kindly suggest that you use the certbot-auto_ method. It's the fastest, the most thoroughly tested and the most reliable way of getting our software and the free -SSL certificates! +TLS/SSL certificates! Beyond the methods discussed here, other methods may be possible, such as installing Certbot directly with pip from PyPI or downloading a ZIP From 6fa7eb576bc5042b8455e1da85c60ee858253dac Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 May 2016 18:11:36 -0700 Subject: [PATCH 298/317] Use -n when using certonly --- docs/using.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 7d7b4fbfd..748fae370 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -254,9 +254,11 @@ renewals of that certificate. An alternative form that provides for more fine-grained control over the renewal process (while renewing specified certificates one at a time), is ``letsencrypt certonly`` with the complete set of subject domains of -a specific certificate specified via `-d` flags, like +a specific certificate specified via `-d` flags. You may also want to +include the ``-n`` or ``--noninteractive`` flag to prevent blocking on +user input (which is useful when running the command from cron). -``letsencrypt certonly -d example.com -d www.example.com`` +``letsencrypt certonly -n -d example.com -d www.example.com`` (All of the domains covered by the certificate must be specified in this case in order to renew and replace the old certificate rather From 3ce47282ea37f2c9b87868ae731c0dcacbef0e12 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 May 2016 18:14:02 -0700 Subject: [PATCH 299/317] Make --quiet suggestion stronger --- docs/using.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 748fae370..e7e0e9474 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -231,9 +231,9 @@ information about renewal hooks can be found by running If you're sure that this command executes successfully without human intervention, you can add the command to ``crontab`` (since certificates are only renewed when they're determined to be near expiry, the command -can run on a regular basis, like every week or every day). You may also -want to use the ``-q`` or ``--quiet`` quiet flag to silence all output -except errors. +can run on a regular basis, like every week or every day). In that case, +you are likely to want to use the ``-q`` or ``--quiet`` quiet flag to +silence all output except errors. The ``--force-renew`` flag may be helpful for automating renewal; it causes the expiration time of the certificate(s) to be ignored when From 7f8fadee37e6c2de78786338d9a30c8a21e9995f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 May 2016 18:33:47 -0700 Subject: [PATCH 300/317] Revert "Make certbot accept --yes flag" This reverts commit a9ecd146a9797eb068827bf6c357fde15f3a9a03. --- certbot/cli.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/certbot/cli.py b/certbot/cli.py index 40af5dccb..e2c57595b 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -649,9 +649,6 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "automation", "--no-self-upgrade", action="store_true", help="(letsencrypt-auto only) prevent the letsencrypt-auto script from" " upgrading itself to newer released versions") - helpful.add( - "automation", "--yes", action="store_true", - help="(letsencrypt-auto only) assume yes is the answer to all prompts") helpful.add( "automation", "-q", "--quiet", dest="quiet", action="store_true", help="Silence all output except errors. Useful for automation via cron." From f38d59d6756f76be4f51b86e1365556da569216f Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 May 2016 19:08:25 -0700 Subject: [PATCH 301/317] Use --non-interactive instead of --yes and use getopt for parsing short opts --- letsencrypt-auto-source/letsencrypt-auto | 31 +++++++++++-------- .../letsencrypt-auto.template | 31 +++++++++++-------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index adeb94e3e..538226d2f 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -29,15 +29,26 @@ all arguments you have provided. Help for certbot itself cannot be provided until it is installed. - --debug attempt installation on experimental platforms - --help print this help - --no-self-upgrade do not download updates for certbot or certbot-auto - --os-packages-only install OS dependencies and exit - -v, --verbose provide more output - --yes assume yes is the answer to all prompts + --debug attempt experimental installation + -h, --help print this help + -n, --non-interactive, --noninteractive run without asking for user input + --no-self-upgrade do not download updates + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output All arguments are accepted and forwarded to the Certbot client when run." +while getopts ":hnv" arg; do + case $arg in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac +done + for arg in "$@" ; do case "$arg" in --debug) @@ -50,16 +61,10 @@ for arg in "$@" ; do NO_SELF_UPGRADE=1;; --help) HELP=1;; - --yes) + --noninteractive|--non-interactive) ASSUME_YES=1;; --verbose) VERBOSE=1;; - [!-]*|-*[!v]*|-) - # Anything that isn't -v, -vv, etc.: that is, anything that does not - # start with a -, contains anything that's not a v, or is just "-" - ;; - *) # -v+ remains. - VERBOSE=1;; esac done diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index fe4baeafd..af4ed62d3 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -29,15 +29,26 @@ all arguments you have provided. Help for certbot itself cannot be provided until it is installed. - --debug attempt installation on experimental platforms - --help print this help - --no-self-upgrade do not download updates for certbot or certbot-auto - --os-packages-only install OS dependencies and exit - -v, --verbose provide more output - --yes assume yes is the answer to all prompts + --debug attempt experimental installation + -h, --help print this help + -n, --non-interactive, --noninteractive run without asking for user input + --no-self-upgrade do not download updates + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output All arguments are accepted and forwarded to the Certbot client when run." +while getopts ":hnv" arg; do + case $arg in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac +done + for arg in "$@" ; do case "$arg" in --debug) @@ -50,16 +61,10 @@ for arg in "$@" ; do NO_SELF_UPGRADE=1;; --help) HELP=1;; - --yes) + --noninteractive|--non-interactive) ASSUME_YES=1;; --verbose) VERBOSE=1;; - [!-]*|-*[!v]*|-) - # Anything that isn't -v, -vv, etc.: that is, anything that does not - # start with a -, contains anything that's not a v, or is just "-" - ;; - *) # -v+ remains. - VERBOSE=1;; esac done From 8394e5eb64e4cd4b18b6432340bc335349a8bec6 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 9 May 2016 19:07:11 -0700 Subject: [PATCH 302/317] Draft of new installation instructions --- README.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 91a3cfcb5..3ba541f77 100644 --- a/README.rst +++ b/README.rst @@ -30,9 +30,15 @@ systems have packages yet, we provide a temporary solution via the ``letsencrypt-auto`` wrapper script, which obtains some dependencies from your OS and puts others in a python virtual environment:: - user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt - user@webserver:~$ cd letsencrypt - user@webserver:~/letsencrypt$ ./letsencrypt-auto --help + user@webserver:~$ wget https://dl.eff.org/certbot-auto + user@webserver:~$ chmod a+x ./certbot-auto + user@webserver:~$ ./certbot-auto --help + +.. hint:: For stronger security, you can use these steps for extra verification before running the script: + + user@webserver:~$ wget https://dl.eff.org/certbot-auto.sig + user@webserver:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 + user@webserver:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.sig certbot-auto Or for full command line help, type:: From 675f2e5413f5e13b8062351ca94f2bfd610498fe Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 9 May 2016 19:24:47 -0700 Subject: [PATCH 303/317] Put the signature instructions in a hint box --- README.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 3ba541f77..040aa6bdf 100644 --- a/README.rst +++ b/README.rst @@ -34,13 +34,14 @@ from your OS and puts others in a python virtual environment:: user@webserver:~$ chmod a+x ./certbot-auto user@webserver:~$ ./certbot-auto --help -.. hint:: For stronger security, you can use these steps for extra verification before running the script: +.. hint:: If you'd like stronger security when downloading the ``certbot-auto`` script, + you can use these steps for extra verification before running it:: - user@webserver:~$ wget https://dl.eff.org/certbot-auto.sig - user@webserver:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 - user@webserver:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.sig certbot-auto + user@server:~$ wget https://dl.eff.org/certbot-auto.sig + user@server:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 + user@server:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.sig certbot-auto -Or for full command line help, type:: +And for full command line help, you can type:: ./letsencrypt-auto --help all From 37efe306754bf8a1f111deb0933a2597ec0a4024 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Mon, 9 May 2016 19:31:29 -0700 Subject: [PATCH 304/317] Better explanation --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 040aa6bdf..840cfff0a 100644 --- a/README.rst +++ b/README.rst @@ -34,8 +34,8 @@ from your OS and puts others in a python virtual environment:: user@webserver:~$ chmod a+x ./certbot-auto user@webserver:~$ ./certbot-auto --help -.. hint:: If you'd like stronger security when downloading the ``certbot-auto`` script, - you can use these steps for extra verification before running it:: +.. hint:: The certbot-auto download is protected by HTTPS, which is pretty good, but if you'd like to + double check the integrity of the ``certbot-auto`` script, you can use these steps for verification before running it:: user@server:~$ wget https://dl.eff.org/certbot-auto.sig user@server:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 From 7a848d2b048b07c1b2fbd5f81b87c2a6cda46359 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Mon, 9 May 2016 19:51:08 -0700 Subject: [PATCH 305/317] Remove unneeded info about backports --- letsencrypt-auto-source/letsencrypt-auto | 3 --- letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh | 3 --- 2 files changed, 6 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 538226d2f..8dbdf5f9c 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -192,9 +192,6 @@ BootstrapDebCommon() { if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then - if echo $BACKPORT_NAME | grep -q wheezy ; then - echo 'Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports"' - fi if [ "$ASSUME_YES" = 1 ]; then /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." sleep 1s diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index e91f52261..bfbcfa31d 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -46,9 +46,6 @@ BootstrapDebCommon() { if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then - if echo $BACKPORT_NAME | grep -q wheezy ; then - echo 'Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports"' - fi if [ "$ASSUME_YES" = 1 ]; then /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." sleep 1s From 86cb5b68e3b2f3148a7d4134ffd786a70c34ee58 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 10 May 2016 10:14:03 -0700 Subject: [PATCH 306/317] cli_command should sometimes be certbot-auto --- certbot/cli.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/certbot/cli.py b/certbot/cli.py index 7ed8a0d3a..90e86a751 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -38,6 +38,11 @@ helpful_parser = None # fails safely LEAUTO = "letsencrypt-auto" +if "CERTBOT_AUTO" in os.environ: + # if we're here, this is probably going to be certbot-auto, unless the + # user saved the script under a different name + LEAUTO = os.path.basename(os.environ["CERTBOT_AUTO"]) + fragment = os.path.join(".local", "share", "letsencrypt") cli_command = LEAUTO if fragment in sys.argv[0] else "certbot" From ed23f2e27fb42aac0b37cc9df74e6853a912e75a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 10 May 2016 10:21:15 -0700 Subject: [PATCH 307/317] CERTBOT_AUTO env was broken (especially if containing spaces) --- letsencrypt-auto-source/letsencrypt-auto | 14 +++++++++++--- letsencrypt-auto-source/letsencrypt-auto.template | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 1f2dfcf85..72adfccb1 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -55,7 +55,7 @@ export CERTBOT_AUTO="$0" if test "`id -u`" -ne "0" ; then if command -v sudo 1>/dev/null 2>&1; then SUDO=sudo - SUDO_ENV="CERTBOT_AUTO=\"$0\"" + SUDO_ENV="CERTBOT_AUTO=$0" else echo \"sudo\" is not available, will use \"su\" for installation steps... # Because the parameters in `su -c` has to be a string, @@ -827,8 +827,16 @@ UNLIKELY_EOF echo "Installation succeeded." fi echo "Requesting root privileges to run certbot..." - echo " " $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" - $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" + if [ -z "$SUDO_ENV" ] ; then + # SUDO is su wrapper / noop + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" + else + # sudo + echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + fi + else # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. # diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 580ee7c07..4e32d8b4f 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -55,7 +55,7 @@ export CERTBOT_AUTO="$0" if test "`id -u`" -ne "0" ; then if command -v sudo 1>/dev/null 2>&1; then SUDO=sudo - SUDO_ENV="CERTBOT_AUTO=\"$0\"" + SUDO_ENV="CERTBOT_AUTO=$0" else echo \"sudo\" is not available, will use \"su\" for installation steps... # Because the parameters in `su -c` has to be a string, @@ -223,8 +223,16 @@ UNLIKELY_EOF echo "Installation succeeded." fi echo "Requesting root privileges to run certbot..." - echo " " $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" - $SUDO $SUDO_ENV "$VENV_BIN/letsencrypt" "$@" + if [ -z "$SUDO_ENV" ] ; then + # SUDO is su wrapper / noop + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" + else + # sudo + echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + fi + else # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. # From 762e9e9db0b2b2eb6711b19b016da740946a63a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20Kr=C3=BCger?= Date: Wed, 11 May 2016 06:16:13 +0200 Subject: [PATCH 308/317] Fix mime.types parsing for nginx Added characters to key parsing rule which appear in the mime type in mime.types. --- certbot-nginx/certbot_nginx/nginxparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot-nginx/certbot_nginx/nginxparser.py b/certbot-nginx/certbot_nginx/nginxparser.py index cef0756d7..8ab7adb0a 100644 --- a/certbot-nginx/certbot_nginx/nginxparser.py +++ b/certbot-nginx/certbot_nginx/nginxparser.py @@ -17,7 +17,7 @@ class RawNginxParser(object): right_bracket = Literal("}").suppress() semicolon = Literal(";").suppress() space = White().suppress() - key = Word(alphanums + "_/") + key = Word(alphanums + "_/+-.") # Matches anything that is not a special character AND any chars in single # or double quotes value = Regex(r"((\".*\")?(\'.*\')?[^\{\};,]?)+") From 22f6b77e680af0144b260a85ff51c53a34689179 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 10 May 2016 18:51:16 -0700 Subject: [PATCH 309/317] Move deprecation warning after logging setup --- certbot/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certbot/main.py b/certbot/main.py index 939a5e0e4..0405d6eb5 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -649,7 +649,6 @@ def main(cli_args=sys.argv[1:]): args = cli.prepare_and_parse_args(plugins, cli_args) config = configuration.NamespaceConfig(args) zope.component.provideUtility(config) - cli.possible_deprecation_warning(config) # Setup logging ASAP, otherwise "No handlers could be found for # logger ..." TODO: this should be done before plugins discovery @@ -662,6 +661,7 @@ def main(cli_args=sys.argv[1:]): le_util.make_or_verify_dir( config.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args) setup_logging(config, _cli_log_handler, logfile='letsencrypt.log') + cli.possible_deprecation_warning(config) logger.debug("certbot version: %s", certbot.__version__) # do not log `config`, as it contains sensitive data (e.g. revoke --key)! From 6f9e28fccad96c19b3fd58560d5c6ddba8201d04 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Wed, 11 May 2016 09:49:23 -0700 Subject: [PATCH 310/317] Allow unrecognized fields in directory. --- acme/acme/messages.py | 11 +---------- acme/acme/messages_test.py | 9 ++++----- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 24a3b580c..4a9bd9f47 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -143,12 +143,6 @@ class Directory(jose.JSONDeSerializable): def __init__(self, jobj): canon_jobj = util.map_keys(jobj, self._canon_key) - if not set(canon_jobj).issubset( - set(self._REGISTERED_TYPES).union(['meta'])): - # TODO: acme-spec is not clear about this: 'It is a JSON - # dictionary, whose keys are the "resource" values listed - # in {{https-requests}}' - raise ValueError('Wrong directory fields') # TODO: check that everything is an absolute URL; acme-spec is # not clear on that self._jobj = canon_jobj @@ -171,10 +165,7 @@ class Directory(jose.JSONDeSerializable): @classmethod def from_json(cls, jobj): jobj['meta'] = cls.Meta.from_json(jobj.pop('meta', {})) - try: - return cls(jobj) - except ValueError as error: - raise jose.DeserializationError(str(error)) + return cls(jobj) class Resource(jose.JSONObjectWithFields): diff --git a/acme/acme/messages_test.py b/acme/acme/messages_test.py index b2b7febdc..b39b9ff55 100644 --- a/acme/acme/messages_test.py +++ b/acme/acme/messages_test.py @@ -97,9 +97,9 @@ class DirectoryTest(unittest.TestCase): ), }) - def test_init_wrong_key_value_error(self): + def test_init_wrong_key_value_success(self): # pylint: disable=no-self-use from acme.messages import Directory - self.assertRaises(ValueError, Directory, {'foo': 'bar'}) + Directory({'foo': 'bar'}) def test_getitem(self): self.assertEqual('reg', self.dir['new-reg']) @@ -127,10 +127,9 @@ class DirectoryTest(unittest.TestCase): }, }) - def test_from_json_deserialization_error_on_wrong_key(self): + def test_from_json_deserialization_unknown_key_success(self): # pylint: disable=no-self-use from acme.messages import Directory - self.assertRaises( - jose.DeserializationError, Directory.from_json, {'foo': 'bar'}) + Directory.from_json({'foo': 'bar'}) class RegistrationTest(unittest.TestCase): From 407ebad36e78c5efa93c56373b2018f07ce31dc5 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 11 May 2016 15:56:10 -0700 Subject: [PATCH 311/317] Support openssl and gpg signatures in parallel --- README.rst | 4 ++-- tools/release.sh | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 840cfff0a..2cec4adad 100644 --- a/README.rst +++ b/README.rst @@ -37,9 +37,9 @@ from your OS and puts others in a python virtual environment:: .. hint:: The certbot-auto download is protected by HTTPS, which is pretty good, but if you'd like to double check the integrity of the ``certbot-auto`` script, you can use these steps for verification before running it:: - user@server:~$ wget https://dl.eff.org/certbot-auto.sig + user@server:~$ wget https://dl.eff.org/certbot-auto.asc user@server:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 - user@server:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.sig certbot-auto + user@server:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.asc certbot-auto And for full command line help, you can type:: diff --git a/tools/release.sh b/tools/release.sh index d41192af9..042aa5259 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -187,6 +187,9 @@ while ! openssl dgst -sha256 -verify $RELEASE_OPENSSL_PUBKEY -signature \ read -p "Please correctly sign letsencrypt-auto with offline-signrequest.sh" done +# This signature is not quite as strong, but easier for people to verify out of band +gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign letsencrypt-auto-source/letsencrypt-auto + # copy leauto to the root, overwriting the previous release version cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto From ba7688ba99cf78f0ef1eff856965e06c4a3d43b3 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 11 May 2016 15:57:38 -0700 Subject: [PATCH 312/317] Avoid certbot-auto.asc.1 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 2cec4adad..74cd838b0 100644 --- a/README.rst +++ b/README.rst @@ -37,7 +37,7 @@ from your OS and puts others in a python virtual environment:: .. hint:: The certbot-auto download is protected by HTTPS, which is pretty good, but if you'd like to double check the integrity of the ``certbot-auto`` script, you can use these steps for verification before running it:: - user@server:~$ wget https://dl.eff.org/certbot-auto.asc + user@server:~$ wget -N https://dl.eff.org/certbot-auto.asc user@server:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 user@server:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.asc certbot-auto From 5214c56f06c5202dfe2c77c15d2534daba17fd4c Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 11 May 2016 16:09:30 -0700 Subject: [PATCH 313/317] Use certbot-auto.asc --- tools/release.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/release.sh b/tools/release.sh index 2e26e3dab..8c2d04cd4 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -189,6 +189,9 @@ done # This signature is not quite as strong, but easier for people to verify out of band gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign letsencrypt-auto-source/letsencrypt-auto +# We can't rename the openssl letsencrypt-auto.sig for compatibility reasons, +# but we can use the right name for cerbot-auto.asc from day one +mv letsencrypt-auto-source/letsencrypt-auto.asc letsencrypt-auto-source/certbot-auto.asc # copy leauto to the root, overwriting the previous release version cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto From 8e742fa3c67a5f50dadd674904ef733ca3044d61 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 11 May 2016 18:04:15 -0700 Subject: [PATCH 314/317] Release 0.6.0 --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-auto | 1088 +++++++++++++++++ certbot-compatibility-test/setup.py | 2 +- certbot-nginx/setup.py | 2 +- certbot/__init__.py | 2 +- letsencrypt-apache/setup.py | 2 +- letsencrypt-auto | 269 ++-- letsencrypt-auto-source/certbot-auto.asc | 11 + letsencrypt-auto-source/letsencrypt-auto | 26 +- letsencrypt-auto-source/letsencrypt-auto.sig | Bin 256 -> 256 bytes .../pieces/letsencrypt-auto-requirements.txt | 24 +- letsencrypt-nginx/setup.py | 2 +- letsencrypt/setup.py | 2 +- letshelp-certbot/setup.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- 16 files changed, 1313 insertions(+), 125 deletions(-) create mode 100755 certbot-auto create mode 100644 letsencrypt-auto-source/certbot-auto.asc diff --git a/acme/setup.py b/acme/setup.py index cbd3bfb87..20c7c7226 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.6.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 7358c7041..538a68139 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.6.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-auto b/certbot-auto new file mode 100755 index 000000000..8c6e6c486 --- /dev/null +++ b/certbot-auto @@ -0,0 +1,1088 @@ +#!/bin/sh +# +# Download and run the latest release version of the Certbot client. +# +# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING +# +# IF YOU WANT TO EDIT IT LOCALLY, *ALWAYS* RUN YOUR COPY WITH THE +# "--no-self-upgrade" FLAG +# +# IF YOU WANT TO SEND PULL REQUESTS, THE REAL SOURCE FOR THIS FILE IS +# letsencrypt-auto-source/letsencrypt-auto.template AND +# letsencrypt-auto-source/pieces/bootstrappers/* + +set -e # Work even if somebody does "sh thisscript.sh". + +# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script, +# if you want to change where the virtual environment will be installed +XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} +VENV_NAME="letsencrypt" +VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} +VENV_BIN="$VENV_PATH/bin" +LE_AUTO_VERSION="0.6.0" +BASENAME=$(basename $0) +USAGE="Usage: $BASENAME [OPTIONS] +A self-updating wrapper script for the Certbot ACME client. When run, updates +to both this script and certbot will be downloaded and installed. After +ensuring you have the latest versions installed, certbot will be invoked with +all arguments you have provided. + +Help for certbot itself cannot be provided until it is installed. + + --debug attempt experimental installation + -h, --help print this help + -n, --non-interactive, --noninteractive run without asking for user input + --no-self-upgrade do not download updates + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output + +All arguments are accepted and forwarded to the Certbot client when run." + +while getopts ":hnv" arg; do + case $arg in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac +done + +for arg in "$@" ; do + case "$arg" in + --debug) + DEBUG=1;; + --os-packages-only) + OS_PACKAGES_ONLY=1;; + --no-self-upgrade) + # Do not upgrade this script (also prevents client upgrades, because each + # copy of the script pins a hash of the python client) + NO_SELF_UPGRADE=1;; + --help) + HELP=1;; + --noninteractive|--non-interactive) + ASSUME_YES=1;; + --verbose) + VERBOSE=1;; + esac +done + +# certbot-auto needs root access to bootstrap OS dependencies, and +# certbot itself needs root access for almost all modes of operation +# The "normal" case is that sudo is used for the steps that need root, but +# this script *can* be run as root (not recommended), or fall back to using +# `su` +SUDO_ENV="" +export CERTBOT_AUTO="$0" +if test "`id -u`" -ne "0" ; then + if command -v sudo 1>/dev/null 2>&1; then + SUDO=sudo + SUDO_ENV="CERTBOT_AUTO=$0" + else + echo \"sudo\" is not available, will use \"su\" for installation steps... + # Because the parameters in `su -c` has to be a string, + # we need properly escape it + su_sudo() { + args="" + # This `while` loop iterates over all parameters given to this function. + # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string + # will be wrapped in a pair of `'`, then appended to `$args` string + # For example, `echo "It's only 1\$\!"` will be escaped to: + # 'echo' 'It'"'"'s only 1$!' + # │ │└┼┘│ + # │ │ │ └── `'s only 1$!'` the literal string + # │ │ └── `\"'\"` is a single quote (as a string) + # │ └── `'It'`, to be concatenated with the strings following it + # └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself + while [ $# -ne 0 ]; do + args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' " + shift + done + su root -c "$args" + } + SUDO=su_sudo + fi +else + SUDO= +fi + +if [ $BASENAME = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + +ExperimentalBootstrap() { + # Arguments: Platform name, bootstrap function name + if [ "$DEBUG" = 1 ]; then + if [ "$2" != "" ]; then + echo "Bootstrapping dependencies via $1..." + $2 + fi + else + echo "WARNING: $1 support is very experimental at present..." + echo "if you would like to work on improving it, please ensure you have backups" + echo "and then run this script again with the --debug flag!" + exit 1 + fi +} + +DeterminePythonVersion() { + for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do + # Break (while keeping the LE_PYTHON value) if found. + command -v "$LE_PYTHON" > /dev/null && break + done + if [ "$?" != "0" ]; then + echo "Cannot find any Pythons; please install one!" + exit 1 + fi + export LE_PYTHON + + PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + if [ "$PYVER" -lt 26 ]; then + echo "You have an ancient version of Python entombed in your operating system..." + echo "This isn't going to work; you'll need at least version 2.6." + exit 1 + fi +} + +BootstrapDebCommon() { + # 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) + + $SUDO apt-get update || echo apt-get update hit problems but continuing anyway... + + # virtualenv binary can be found in different packages depending on + # distro version (#346) + + virtualenv= + if apt-cache show virtualenv > /dev/null 2>&1; then + virtualenv="virtualenv" + fi + + if apt-cache show python-virtualenv > /dev/null 2>&1; then + virtualenv="$virtualenv python-virtualenv" + fi + + augeas_pkg="libaugeas0 augeas-lenses" + AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + + if [ "$ASSUME_YES" = 1 ]; then + YES_FLAG="-y" + fi + + AddBackportRepo() { + # ARGS: + BACKPORT_NAME="$1" + BACKPORT_SOURCELINE="$2" + echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME." + if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then + # This can theoretically error if sources.list.d is empty, but in that case we don't care. + if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then + if [ "$ASSUME_YES" = 1 ]; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s + add_backports=1 + else + read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response + case $response in + [yY][eE][sS]|[yY]|"") + add_backports=1;; + *) + add_backports=0;; + esac + fi + if [ "$add_backports" = 1 ]; then + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update + fi + fi + fi + if [ "$add_backports" != 0 ]; then + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + fi + } + + + if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then + if lsb_release -a | grep -q wheezy ; then + AddBackportRepo wheezy-backports "deb http://http.debian.net/debian wheezy-backports main" + elif lsb_release -a | grep -q precise ; then + # XXX add ARM case + AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" + else + echo "No libaugeas0 version is available that's new enough to run the" + echo "Certbot apache plugin..." + fi + # XXX add a case for ubuntu PPAs + fi + + $SUDO apt-get install $YES_FLAG --no-install-recommends \ + python \ + python-dev \ + $virtualenv \ + gcc \ + dialog \ + $augeas_pkg \ + libssl-dev \ + libffi-dev \ + ca-certificates \ + + + + if ! command -v virtualenv > /dev/null ; then + echo Failed to install a working \"virtualenv\" command, exiting + exit 1 + fi +} + +BootstrapRpmCommon() { + # Tested with: + # - Fedora 20, 21, 22, 23 (x64) + # - Centos 7 (x64: on DigitalOcean droplet) + # - CentOS 7 Minimal install in a Hyper-V VM + # - CentOS 6 (EPEL must be installed manually) + + if type dnf 2>/dev/null + then + tool=dnf + elif type yum 2>/dev/null + then + tool=yum + + else + echo "Neither yum nor dnf found. Aborting bootstrap!" + exit 1 + fi + + pkgs=" + gcc + dialog + augeas-libs + openssl + openssl-devel + libffi-devel + redhat-rpm-config + ca-certificates + " + + # Some distros and older versions of current distros use a "python27" + # instead of "python" naming convention. Try both conventions. + if $SUDO $tool list python >/dev/null 2>&1; then + pkgs="$pkgs + python + python-devel + python-virtualenv + python-tools + python-pip + " + else + pkgs="$pkgs + python27 + python27-devel + python27-virtualenv + python27-tools + python27-pip + " + fi + + if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then + pkgs="$pkgs + mod_ssl + " + fi + + if [ "$ASSUME_YES" = 1 ]; then + yes_flag="-y" + fi + + if ! $SUDO $tool install $yes_flag $pkgs; then + echo "Could not install OS dependencies. Aborting bootstrap!" + exit 1 + fi +} + +BootstrapSuseCommon() { + # SLE12 don't have python-virtualenv + + if [ "$ASSUME_YES" = 1 ]; then + zypper_flags="-nq" + install_flags="-l" + fi + + $SUDO zypper $zypper_flags in $install_flags \ + python \ + python-devel \ + python-virtualenv \ + gcc \ + dialog \ + augeas-lenses \ + libopenssl-devel \ + libffi-devel \ + ca-certificates +} + +BootstrapArchCommon() { + # Tested with: + # - ArchLinux (x86_64) + # + # "python-virtualenv" is Python3, but "python2-virtualenv" provides + # only "virtualenv2" binary, not "virtualenv" necessary in + # ./tools/_venv_common.sh + + deps=" + python2 + python-virtualenv + gcc + dialog + augeas + openssl + libffi + ca-certificates + pkg-config + " + + # pacman -T exits with 127 if there are missing dependencies + missing=$($SUDO pacman -T $deps) || true + + if [ "$ASSUME_YES" = 1 ]; then + noconfirm="--noconfirm" + fi + + if [ "$missing" ]; then + $SUDO pacman -S --needed $missing $noconfirm + fi +} + +BootstrapGentooCommon() { + PACKAGES=" + 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) + $SUDO cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x + ;; + (pkgcore) + $SUDO pmerge --noreplace --oneshot $PACKAGES + ;; + (portage|*) + $SUDO emerge --noreplace --oneshot $PACKAGES + ;; + esac +} + +BootstrapFreeBsd() { + $SUDO pkg install -Ay \ + python \ + py27-virtualenv \ + augeas \ + libffi +} + +BootstrapMac() { + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" + fi + + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python + fi + + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib + fi + + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python + fi + + if ! hash virtualenv 2>/dev/null; then + echo "virtualenv not installed." + echo "Installing with pip..." + pip install virtualenv + fi +} + + +# Install required OS packages: +Bootstrap() { + if [ -f /etc/debian_version ]; then + echo "Bootstrapping dependencies for Debian-based OSes..." + BootstrapDebCommon + elif [ -f /etc/redhat-release ]; then + echo "Bootstrapping dependencies for RedHat-based OSes..." + BootstrapRpmCommon + elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + echo "Bootstrapping dependencies for openSUSE-based OSes..." + BootstrapSuseCommon + elif [ -f /etc/arch-release ]; then + if [ "$DEBUG" = 1 ]; then + echo "Bootstrapping dependencies for Archlinux..." + BootstrapArchCommon + else + echo "Please use pacman to install letsencrypt packages:" + echo "# pacman -S letsencrypt letsencrypt-apache" + echo + echo "If you would like to use the virtualenv way, please run the script again with the" + echo "--debug flag." + exit 1 + fi + elif [ -f /etc/manjaro-release ]; then + ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon + elif [ -f /etc/gentoo-release ]; then + ExperimentalBootstrap "Gentoo" BootstrapGentooCommon + elif uname | grep -iq FreeBSD ; then + ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd + elif uname | grep -iq Darwin ; then + ExperimentalBootstrap "Mac OS X" BootstrapMac + elif grep -iq "Amazon Linux" /etc/issue ; then + ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon + else + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" + echo + echo "You will need to bootstrap, configure virtualenv, and run pip install manually." + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "for more info." + fi +} + +TempDir() { + mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || OS X +} + + + +if [ "$1" = "--le-auto-phase2" ]; then + # Phase 2: Create venv, install LE, and run. + + shift 1 # the --le-auto-phase2 arg + if [ -f "$VENV_BIN/letsencrypt" ]; then + # --version output ran through grep due to python-cryptography DeprecationWarnings + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) + else + INSTALLED_VERSION="none" + fi + if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then + echo "Creating virtual environment..." + DeterminePythonVersion + rm -rf "$VENV_PATH" + if [ "$VERBOSE" = 1 ]; then + virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" + else + virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" > /dev/null + fi + + echo "Installing Python packages..." + TEMP_DIR=$(TempDir) + # There is no $ interpolation due to quotes on starting heredoc delimiter. + # ------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" +# This is the flattened list of packages certbot-auto installs. To generate +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and +# then use `hashin` or a more secure method to gather the hashes. + +argparse==1.4.0 \ + --hash=sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314 \ + --hash=sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4 + +# This comes before cffi because cffi will otherwise install an unchecked +# version via setup_requires. +pycparser==2.14 \ + --hash=sha256:7959b4a74abdc27b312fed1c21e6caf9309ce0b29ea86b591fd2e99ecdf27f73 + +cffi==1.4.2 \ + --hash=sha256:53c1c9ddb30431513eb7f3cdef0a3e06b0f1252188aaa7744af0f5a4cd45dbaf \ + --hash=sha256:a568f49dfca12a8d9f370187257efc58a38109e1eee714d928561d7a018a64f8 \ + --hash=sha256:809c6ca8cfbcaeebfbd432b4576001b40d38ff2463773cb57577d75e1a020bc3 \ + --hash=sha256:86cdca2cd9cba41422230390df17dfeaa9f344a911e3975c8be9da57b35548e9 \ + --hash=sha256:24b13db84aec385ca23c7b8ded83ef8bb4177bc181d14758f9f975be5d020d86 \ + --hash=sha256:969aeffd7c0e097f6be1efd682c156ae226591a0793a94b6c2d5e4293f4c8d4e \ + --hash=sha256:000f358d4b0fa249feaab9c1ce7d5b2fe7e02e7bdf6806c26418505fc685e268 \ + --hash=sha256:a9d86f460bbd8358a2d513ad779e3f3fc878e3b93a00b5002faebf616ffe6b9c \ + --hash=sha256:3127b3ab33eb23ccac071f9a0802748e5cf7c5cbcd02482bb063e35b41dbb0b0 \ + --hash=sha256:e2b2d42236469a40224d39e7b6c60575f388b2f423f354c7ee90a5b7f58c8065 \ + --hash=sha256:8c2dccafee89b1b424b0bec6ad2dd9622c949d2024e929f5da1ed801eac75f1d \ + --hash=sha256:a4de7a4d11aed488bab4fb14f4988587a829bece5a20433f780d6e33b08083cb \ + --hash=sha256:5ca8fe30425265a49274e4b0213a1bc98f4b13449ae5e96f984771e5d83e58c1 \ + --hash=sha256:a4fd38802f59e714eba81a024f62db710b27dbe27a7ea12e911537327aa84d30 \ + --hash=sha256:86cd6912bbc83e9405d4a73cd7f4b4ee8353652d2dbc7c820106ed5b4d1bab3a \ + --hash=sha256:8f1d177d364ea35900415ae24ca3e471be3d5334ed0419294068c49f45913998 +ConfigArgParse==0.10.0 \ + --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +configobj==5.0.6 \ + --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 +cryptography==1.2.3 \ + --hash=sha256:031938f73a5c5eb3e809e18ff7caeb6865351871417be6050cb8c86a9a202b9a \ + --hash=sha256:a179a38d50f8d68b491d7a313db78f8cabe290842cecddddc7b34d408e59db0a \ + --hash=sha256:906c88b2aadcf99cfabb24098263d1bf65ab0c8688acde10dae1f09d865920f1 \ + --hash=sha256:6e706c5c6088770b1d1b634e959e21963e315b0255f5f4777125ad3d54082977 \ + --hash=sha256:f5ebf8e31c48f8707921dca0e994de77813a9c9b9bf03c119c5ddf97bdcffe73 \ + --hash=sha256:c7b89e42288cc7fbee3812e99ef5c744f22452e11d6822f6807afc6d6b3be83e \ + --hash=sha256:8408d29865947109d8b68f1837a7cde1aa4dc86e0f79ca3ba58c0c44e443d6a5 \ + --hash=sha256:c7e76cf3c3d925dd31fa238cfb806cffba718c0f08707d77a538768477969956 \ + --hash=sha256:7d8de35380f31702758b7753bb5c40723832c73006dedb2f9099bf61a37f7287 \ + --hash=sha256:5edbee71fae5469ee83fe0a37866b9398c8ce3a46325c24fcedfbf097bb48a19 \ + --hash=sha256:594edafe4801c13bdc1cc305e7704a90c19617e95936f6ab457ee4ffe000ba50 \ + --hash=sha256:b7fdb16a0a7f481be42da744bfe1ea2163025de21f90f2c688a316f3c354da9c \ + --hash=sha256:207b8bf0fe0907336df38b733b487521cf9e138189aba9234ad54fe545dd0db8 \ + --hash=sha256:509a2f05386270cf783993c90d49ffefb3dd62aee45bf1ea8ce3d2cde7271c21 \ + --hash=sha256:ac69b65dd1af0179ede40c9f15788c88f73e628ea6c0519de3838e279bb388c6 \ + --hash=sha256:8df6fad6c6ae12fd7004ea29357f0a2b4d3774eaeca7656530d08d2d90cd41aa \ + --hash=sha256:0b8b96dd81cc1533a04f30382c0fe21c1972e189f794d0c4261a18cec08fd9b5 \ + --hash=sha256:cae8fca1883f23c50ea78d89de6fe4fefdb4cea83177760f47177559414ded93 \ + --hash=sha256:1a471ca576a9cdce1b1cd9f3a22b1d09ee44d46862037557de17919c0db44425 \ + --hash=sha256:8ec4e8e3d453b3a1b63b5f57737a434dcf1ee4a2f26f6ff7c5a37c3f679104d2 \ + --hash=sha256:8eb11c77dd8e73f48df6b2f7a7e16173fe0fe8fdfe266232832e88477e08454e +enum34==1.1.2 \ + --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ + --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 +funcsigs==0.4 \ + --hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \ + --hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033 +idna==2.0 \ + --hash=sha256:9b2fc50bd3c4ba306b9651b69411ef22026d4d8335b93afc2214cef1246ce707 \ + --hash=sha256:16199aad938b290f5be1057c0e1efc6546229391c23cea61ca940c115f7d3d3b +ipaddress==1.0.16 \ + --hash=sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5 \ + --hash=sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0 +linecache2==1.0.0 \ + --hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \ + --hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c +ndg-httpsclient==0.4.0 \ + --hash=sha256:e8c155fdebd9c4bcb0810b4ed01ae1987554b1ee034dd7532d7b8fdae38a6274 +ordereddict==1.1 \ + --hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f +parsedatetime==2.1 \ + --hash=sha256:ce9d422165cf6e963905cd5f74f274ebf7cc98c941916169178ef93f0e557838 \ + --hash=sha256:17c578775520c99131634e09cfca5a05ea9e1bd2a05cd06967ebece10df7af2d +pbr==1.8.1 \ + --hash=sha256:46c8db75ae75a056bd1cc07fa21734fe2e603d11a07833ecc1eeb74c35c72e0c \ + --hash=sha256:e2127626a91e6c885db89668976db31020f0af2da728924b56480fc7ccf09649 +psutil==3.3.0 \ + --hash=sha256:584f0b29fcc5d523b433cb8918b2fc74d67e30ee0b44a95baf031528f424619f \ + --hash=sha256:28ca0b6e9d99aa8dc286e8747a4471362b69812a25291de29b6a8d70a1545a0d \ + --hash=sha256:167ad5fff52a672c4ddc1c1a0b25146d6813ebb08a9aab0a3ac45f8a5b669c3b \ + --hash=sha256:e6dea6173a988727bb223d3497349ad5cdef5c0b282eff2d83e5f9065c53f85f \ + --hash=sha256:2af5e0a4aad66049955d0734aa4e3dc8caa17a9eaf8b4c1a27a5f1ee6e40f6fc \ + --hash=sha256:d9884dc0dc2e55e2448e495778dc9899c1c8bf37aeb2f434c1bea74af93c2683 \ + --hash=sha256:e27c2fe6dfcc8738be3d2c5a022f785eb72971057e1a9e1e34fba73bce8a71a6 \ + --hash=sha256:65afd6fecc8f3aed09ee4be63583bc8eb472f06ceaa4fe24c4d1d5a1a3c0e13f \ + --hash=sha256:ba1c558fbfcdf94515c2394b1155c1dc56e2bc2a9c17d30349827c9ed8a67e46 \ + --hash=sha256:ba95ea0022dcb64d36f0c1335c0605fae35bdf3e0fea8d92f5d0f6456a35e55b \ + --hash=sha256:421b6591d16b509aaa8d8c15821d66bb94cb4a8dc4385cad5c51b85d4a096d85 \ + --hash=sha256:326b305cbdb6f94dafbfe2c26b11da88b0ab07b8a07f8188ab9d75ff0c6e841a \ + --hash=sha256:9aede5b2b6fe46b3748ea8e5214443890d1634027bef3d33b7dad16556830278 \ + --hash=sha256:73bed1db894d1aa9c3c7e611d302cdeab7ae8a0dc0eeaf76727878db1ac5cd87 \ + --hash=sha256:935b5dd6d558af512f42501a7c08f41d7aff139af1bb3959daa3abb859234d6c \ + --hash=sha256:4ca0111cf157dcc0f2f69a323c5b5478718d68d45fc9435d84be0ec0f186215b \ + --hash=sha256:b6f13c95398a3fcf0226c4dcfa448560ba5865259cd96ec2810658651e932189 \ + --hash=sha256:ee6be30d1635bbdea4c4325d507dc8a0dbbde7e1c198bd62ddb9f43198b9e214 \ + --hash=sha256:dfa786858c268d7fbbe1b6175e001ec02738d7cfae0a7ce77bf9b651af676729 \ + --hash=sha256:aa77f9de72af9c16cc288cd4a24cf58824388f57d7a81e400c4616457629870e \ + --hash=sha256:f500093357d04da8140d87932cac2e54ef592a54ca8a743abb2850f60c2c22eb +pyasn1==0.1.9 \ + --hash=sha256:61f9d99e3cef65feb1bfe3a2eef7a93eb93819d345bf54bcd42f4e63d5204dae \ + --hash=sha256:1802a6dd32045e472a419db1441aecab469d33e0d2749e192abdec52101724af \ + --hash=sha256:35025cd9422c96504912f04e2f15fe79390a8597b430c2ca5d0534cf9309ffa0 \ + --hash=sha256:2f96ed5a0c329ca16230b326ca12b7461ec8f65e0be3e4f997516f36bf82a345 \ + --hash=sha256:28fee44217991cfad9e6a0b9f7e3f26041e21ebc96629e94e585ccd05d49fa65 \ + --hash=sha256:326e7a854a17fab07691204747695f8f692d674588a355c441fb14f660bf4e68 \ + --hash=sha256:cda5a90485709ca6795c86056c3e5fe7266028b05e53f1d527fdf93a6365a6b8 \ + --hash=sha256:0cb2a14742b543fdd68f931a14ce3829186ed2b1b2267a06787388c96b2dd9be \ + --hash=sha256:5191ff6b9126d2c039dd87f8ff025bed274baf07fa78afa46f556b1ad7265d6e \ + --hash=sha256:8323e03637b2d072cc7041300bac6ec448c3c28950ab40376036788e9a1af629 \ + --hash=sha256:853cacd96d1f701ddd67aa03ecc05f51890135b7262e922710112f12a2ed2a7f +pyOpenSSL==0.15.1 \ + --hash=sha256:88e45e6bb25dfed272a1ef2e728461d44b634c2cd689e989b6e56a349c5a3ae5 \ + --hash=sha256:f0a26070d6db0881de8bcc7846934b7c3c930d8f9c79d45883ee48984bc0d672 +pyRFC3339==1.0 \ + --hash=sha256:eea31835c56e2096af4363a5745a784878a61d043e247d3a6d6a0a32a9741f56 \ + --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 +python-augeas==0.5.0 \ + --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 +python2-pythondialog==3.3.0 \ + --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ + --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa +pytz==2015.7 \ + --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ + --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ + --hash=sha256:ead4aefa7007249e05e51b01095719d5a8dd95760089f5730aac5698b1932918 \ + --hash=sha256:3cca0df08bd0ed98432390494ce3ded003f5e661aa460be7a734bffe35983605 \ + --hash=sha256:3ede470d3d17ba3c07638dfa0d10452bc1b6e5ad326127a65ba77e6aaeb11bec \ + --hash=sha256:68c47964f7186eec306b13629627722b9079cd4447ed9e5ecaecd4eac84ca734 \ + --hash=sha256:dd5d3991950aae40a6c81de1578942e73d629808cefc51d12cd157980e6cfc18 \ + --hash=sha256:a77c52062c07eb7c7b30545dbc73e32995b7e117eea750317b5cb5c7a4618f14 \ + --hash=sha256:81af9aec4bc960a9a0127c488f18772dae4634689233f06f65443e7b11ebeb51 \ + --hash=sha256:e079b1dadc5c06246cc1bb6fe1b23a50b1d1173f2edd5104efd40bb73a28f406 \ + --hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \ + --hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \ + --hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3 +requests==2.9.1 \ + --hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \ + --hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f +six==1.10.0 \ + --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \ + --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a +traceback2==1.4.0 \ + --hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23 \ + --hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 +unittest2==1.1.0 \ + --hash=sha256:13f77d0875db6d9b435e1d4f41e74ad4cc2eb6e1d5c824996092b3430f088bb8 \ + --hash=sha256:22882a0e418c284e1f718a822b3b022944d53d2d908e1690b319a9d3eb2c0579 +zope.component==4.2.2 \ + --hash=sha256:282c112b55dd8e3c869a3571f86767c150ab1284a9ace2bdec226c592acaf81a +zope.event==4.1.0 \ + --hash=sha256:dc7a59a2fd91730d3793131a5d261b29e93ec4e2a97f1bc487ce8defee2fe786 +zope.interface==4.1.3 \ + --hash=sha256:f07b631f7a601cd8cbd3332d54f43142c7088a83299f859356f08d1d4d4259b3 \ + --hash=sha256:de5cca083b9439d8002fb76bbe6b4998c5a5a721fab25b84298967f002df4c94 \ + --hash=sha256:6788416f7ea7f5b8a97be94825377aa25e8bdc73463e07baaf9858b29e737077 \ + --hash=sha256:6f3230f7254518201e5a3708cbb2de98c848304f06e3ded8bfb39e5825cba2e1 \ + --hash=sha256:5fa575a5240f04200c3088427d0d4b7b737f6e9018818a51d8d0f927a6a2517a \ + --hash=sha256:522194ad6a545735edd75c8a83f48d65d1af064e432a7d320d64f56bafc12e99 \ + --hash=sha256:e8c7b2d40943f71c99148c97f66caa7f5134147f57423f8db5b4825099ce9a09 \ + --hash=sha256:279024f0208601c3caa907c53876e37ad88625f7eaf1cb3842dbe360b2287017 \ + --hash=sha256:2e221a9eec7ccc58889a278ea13dcfed5ef939d80b07819a9a8b3cb1c681484f \ + --hash=sha256:69118965410ec86d44dc6b9017ee3ddbd582e0c0abeef62b3a19dbf6c8ad132b \ + --hash=sha256:d04df8686ec864d0cade8cf199f7f83aecd416109a20834d568f8310ded12dea \ + --hash=sha256:e75a947e15ee97e7e71e02ea302feb2fc62d3a2bb4668bf9dfbed43a506ac7e7 \ + --hash=sha256:4e45d22fb883222a5ab9f282a116fec5ee2e8d1a568ccff6a2d75bbd0eb6bcfc \ + --hash=sha256:bce9339bb3c7a55e0803b63d21c5839e8e479bc85c4adf42ae415b72f94facb2 \ + --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ + --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ + --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 +mock==1.0.1 \ + --hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \ + --hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc + +# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. + +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 + +UNLIKELY_EOF + # ------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" +#!/usr/bin/env python +"""A small script that can act as a trust root for installing pip 8 + +Embed this in your project, and your VCS checkout is all you have to trust. In +a post-peep era, this lets you claw your way to a hash-checking version of pip, +with which you can install the rest of your dependencies safely. All it assumes +is Python 2.6 or better and *some* version of pip already installed. If +anything goes wrong, it will exit with a non-zero status code. + +""" +# This is here so embedded copies are MIT-compliant: +# Copyright (c) 2016 Erik Rose +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +from __future__ import print_function +from hashlib import sha256 +from os.path import join +from pipes import quote +from shutil import rmtree +try: + from subprocess import check_output +except ImportError: + from subprocess import CalledProcessError, PIPE, Popen + + def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be ' + 'overridden.') + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return output +from sys import exit, version_info +from tempfile import mkdtemp +try: + from urllib2 import build_opener, HTTPHandler, HTTPSHandler +except ImportError: + from urllib.request import build_opener, HTTPHandler, HTTPSHandler +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse # 3.4 + + +__version__ = 1, 1, 1 + + +# wheel has a conditional dependency on argparse: +maybe_argparse = ( + [('https://pypi.python.org/packages/source/a/argparse/' + 'argparse-1.4.0.tar.gz', + '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] + if version_info < (2, 7, 0) else []) + + +PACKAGES = maybe_argparse + [ + # Pip has no dependencies, as it vendors everything: + ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', + '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + # This version of setuptools has only optional dependencies: + ('https://pypi.python.org/packages/source/s/setuptools/' + 'setuptools-20.2.2.tar.gz', + '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), + ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') +] + + +class HashError(Exception): + def __str__(self): + url, path, actual, expected = self.args + return ('{url} did not match the expected hash {expected}. Instead, ' + 'it was {actual}. The file (left at {path}) may have been ' + 'tampered with.'.format(**locals())) + + +def hashed_download(url, temp, digest): + """Download ``url`` to ``temp``, make sure it has the SHA-256 ``digest``, + and return its path.""" + # Based on pip 1.4.1's URLOpener but with cert verification removed. Python + # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert + # authenticity has only privacy (not arbitrary code execution) + # implications, since we're checking hashes. + def opener(): + opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) + return opener + + def read_chunks(response, chunk_size): + while True: + chunk = response.read(chunk_size) + if not chunk: + break + yield chunk + + response = opener().open(url) + path = join(temp, urlparse(url).path.split('/')[-1]) + actual_hash = sha256() + with open(path, 'wb') as file: + for chunk in read_chunks(response, 4096): + file.write(chunk) + actual_hash.update(chunk) + + actual_digest = actual_hash.hexdigest() + if actual_digest != digest: + raise HashError(url, path, actual_digest, digest) + return path + + +def main(): + temp = mkdtemp(prefix='pipstrap-') + try: + downloads = [hashed_download(url, temp, digest) + for url, digest in PACKAGES] + check_output('pip install --no-index --no-deps -U ' + + ' '.join(quote(d) for d in downloads), + shell=True) + except HashError as exc: + print(exc) + except Exception: + rmtree(temp) + raise + else: + rmtree(temp) + return 0 + return 1 + + +if __name__ == '__main__': + exit(main()) + +UNLIKELY_EOF + # ------------------------------------------------------------------------- + # Set PATH so pipstrap upgrades the right (v)env: + PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" + set +e + PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_STATUS=$? + set -e + rm -rf "$TEMP_DIR" + if [ "$PIP_STATUS" != 0 ]; then + # Report error. (Otherwise, be quiet.) + echo "Had a problem while installing Python packages:" + echo "$PIP_OUT" + rm -rf "$VENV_PATH" + exit 1 + fi + echo "Installation succeeded." + fi + echo "Requesting root privileges to run certbot..." + if [ -z "$SUDO_ENV" ] ; then + # SUDO is su wrapper / noop + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" + else + # sudo + echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + fi + +else + # Phase 1: Upgrade certbot-auto if neceesary, then self-invoke. + # + # Each phase checks the version of only the thing it is responsible for + # upgrading. Phase 1 checks the version of the latest release of + # certbot-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. + + if [ ! -f "$VENV_BIN/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi + # If it looks like we've never bootstrapped before, bootstrap: + Bootstrap + fi + if [ "$OS_PACKAGES_ONLY" = 1 ]; then + echo "OS packages installed." + exit 0 + fi + + if [ "$NO_SELF_UPGRADE" != 1 ]; then + echo "Checking for new version..." + TEMP_DIR=$(TempDir) + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" +"""Do downloading and JSON parsing without additional dependencies. :: + + # Print latest released version of LE to stdout: + python fetch.py --latest-version + + # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm + # in, and make sure its signature verifies: + python fetch.py --le-auto-script v1.2.3 + +On failure, return non-zero. + +""" +from distutils.version import LooseVersion +from json import loads +from os import devnull, environ +from os.path import dirname, join +import re +from subprocess import check_call, CalledProcessError +from sys import argv, exit +from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError + +PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq +OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18 +xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp +9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij +n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH +cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+ +CQIDAQAB +-----END PUBLIC KEY----- +""") + +class ExpectedError(Exception): + """A novice-readable exception that also carries the original exception for + debugging""" + + +class HttpsGetter(object): + def __init__(self): + """Build an HTTPS opener.""" + # Based on pip 1.4.1's URLOpener + # This verifies certs on only Python >=2.7.9. + self._opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in self._opener.handlers: + if isinstance(handler, HTTPHandler): + self._opener.handlers.remove(handler) + + def get(self, url): + """Return the document contents pointed to by an HTTPS URL. + + If something goes wrong (404, timeout, etc.), raise ExpectedError. + + """ + try: + return self._opener.open(url).read() + except (HTTPError, IOError) as exc: + raise ExpectedError("Couldn't download %s." % url, exc) + + +def write(contents, dir, filename): + """Write something to a file in a certain directory.""" + with open(join(dir, filename), 'w') as file: + file.write(contents) + + +def latest_stable_version(get): + """Return the latest stable release of letsencrypt.""" + metadata = loads(get( + environ.get('LE_AUTO_JSON_URL', + 'https://pypi.python.org/pypi/letsencrypt/json'))) + # metadata['info']['version'] actually returns the latest of any kind of + # release release, contrary to https://wiki.python.org/moin/PyPIJSON. + # The regex is a sufficient regex for picking out prereleases for most + # packages, LE included. + return str(max(LooseVersion(r) for r + in metadata['releases'].iterkeys() + if re.match('^[0-9.]+$', r))) + + +def verified_new_le_auto(get, tag, temp_dir): + """Return the path to a verified, up-to-date letsencrypt-auto script. + + If the download's signature does not verify or something else goes wrong + with the verification process, raise ExpectedError. + + """ + le_auto_dir = environ.get( + 'LE_AUTO_DIR_TEMPLATE', + 'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/' + 'letsencrypt-auto-source/') % tag + write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto') + write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig') + write(PUBLIC_KEY, temp_dir, 'public_key.pem') + try: + with open(devnull, 'w') as dev_null: + check_call(['openssl', 'dgst', '-sha256', '-verify', + join(temp_dir, 'public_key.pem'), + '-signature', + join(temp_dir, 'letsencrypt-auto.sig'), + join(temp_dir, 'letsencrypt-auto')], + stdout=dev_null, + stderr=dev_null) + except CalledProcessError as exc: + raise ExpectedError("Couldn't verify signature of downloaded " + "certbot-auto.", exc) + + +def main(): + get = HttpsGetter().get + flag = argv[1] + try: + if flag == '--latest-version': + print latest_stable_version(get) + elif flag == '--le-auto-script': + tag = argv[2] + verified_new_le_auto(get, tag, dirname(argv[0])) + except ExpectedError as exc: + print exc.args[0], exc.args[1] + return 1 + else: + return 0 + + +if __name__ == '__main__': + exit(main()) + +UNLIKELY_EOF + # --------------------------------------------------------------------------- + DeterminePythonVersion + if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then + echo "WARNING: unable to check for updates." + elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + + # Now we drop into Python so we don't have to install even more + # dependencies (curl, etc.), for better flow control, and for the option of + # future Windows compatibility. + "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" + + # Install new copy of certbot-auto. + # TODO: Deal with quotes in pathnames. + echo "Replacing certbot-auto..." + # Clone permissions with cp. chmod and chown don't have a --reference + # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: + $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" + $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" + # Using mv rather than cp leaves the old file descriptor pointing to the + # original copy so the shell can continue to read it unmolested. mv across + # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the + # cp is unlikely to fail (esp. under sudo) if the rm doesn't. + $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" + # TODO: Clean up temp dir safely, even if it has quotes in its path. + rm -rf "$TEMP_DIR" + fi # A newer version is available. + fi # Self-upgrading is allowed. + + "$0" --le-auto-phase2 "$@" +fi diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index c62a10f89..ee2e128dd 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.6.0' install_requires = [ 'certbot=={0}'.format(version), diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 0e5c27a0a..634092fff 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.6.0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot/__init__.py b/certbot/__init__.py index a48d62548..84022010b 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.6.0.dev0' +__version__ = '0.6.0' diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index a52044f87..1718cc5b4 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0.dev0' +version = '0.6.0' # This package is a simple shim around certbot-apache diff --git a/letsencrypt-auto b/letsencrypt-auto index 942fd8ea2..8c6e6c486 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -1,6 +1,6 @@ #!/bin/sh # -# Download and run the latest release version of the Let's Encrypt client. +# Download and run the latest release version of the Certbot client. # # NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING # @@ -19,11 +19,36 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.5.0" +LE_AUTO_VERSION="0.6.0" +BASENAME=$(basename $0) +USAGE="Usage: $BASENAME [OPTIONS] +A self-updating wrapper script for the Certbot ACME client. When run, updates +to both this script and certbot will be downloaded and installed. After +ensuring you have the latest versions installed, certbot will be invoked with +all arguments you have provided. + +Help for certbot itself cannot be provided until it is installed. + + --debug attempt experimental installation + -h, --help print this help + -n, --non-interactive, --noninteractive run without asking for user input + --no-self-upgrade do not download updates + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output + +All arguments are accepted and forwarded to the Certbot client when run." + +while getopts ":hnv" arg; do + case $arg in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac +done -# This script takes the same arguments as the main letsencrypt program, but it -# additionally responds to --verbose (more output) and --debug (allow support -# for experimental platforms) for arg in "$@" ; do case "$arg" in --debug) @@ -34,25 +59,26 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --help) + HELP=1;; + --noninteractive|--non-interactive) + ASSUME_YES=1;; --verbose) VERBOSE=1;; - [!-]*|-*[!v]*|-) - # Anything that isn't -v, -vv, etc.: that is, anything that does not - # start with a -, contains anything that's not a v, or is just "-" - ;; - *) # -v+ remains. - VERBOSE=1;; esac done -# letsencrypt-auto needs root access to bootstrap OS dependencies, and -# letsencrypt itself needs root access for almost all modes of operation +# certbot-auto needs root access to bootstrap OS dependencies, and +# certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` +SUDO_ENV="" +export CERTBOT_AUTO="$0" if test "`id -u`" -ne "0" ; then if command -v sudo 1>/dev/null 2>&1; then SUDO=sudo + SUDO_ENV="CERTBOT_AUTO=$0" else echo \"sudo\" is not available, will use \"su\" for installation steps... # Because the parameters in `su -c` has to be a string, @@ -81,6 +107,12 @@ else SUDO= fi +if [ $BASENAME = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + ExperimentalBootstrap() { # Arguments: Platform name, bootstrap function name if [ "$DEBUG" = 1 ]; then @@ -151,30 +183,45 @@ BootstrapDebCommon() { augeas_pkg="libaugeas0 augeas-lenses" AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + if [ "$ASSUME_YES" = 1 ]; then + YES_FLAG="-y" + fi + AddBackportRepo() { # ARGS: BACKPORT_NAME="$1" BACKPORT_SOURCELINE="$2" + echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME." if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then - /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." - sleep 1s - /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." - sleep 1s - /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." - sleep 1s - if echo $BACKPORT_NAME | grep -q wheezy ; then - /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + if [ "$ASSUME_YES" = 1 ]; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s + add_backports=1 + else + read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response + case $response in + [yY][eE][sS]|[yY]|"") + add_backports=1;; + *) + add_backports=0;; + esac + fi + if [ "$add_backports" = 1 ]; then + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update fi - - $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" - $SUDO apt-get update fi fi - $SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg - augeas_pkg= - + if [ "$add_backports" != 0 ]; then + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + fi } @@ -186,12 +233,12 @@ BootstrapDebCommon() { AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" else echo "No libaugeas0 version is available that's new enough to run the" - echo "Let's Encrypt apache plugin..." + echo "Certbot apache plugin..." fi # XXX add a case for ubuntu PPAs fi - $SUDO apt-get install -y --no-install-recommends \ + $SUDO apt-get install $YES_FLAG --no-install-recommends \ python \ python-dev \ $virtualenv \ @@ -212,9 +259,10 @@ BootstrapDebCommon() { BootstrapRpmCommon() { # Tested with: - # - Fedora 22, 23 (x64) + # - Fedora 20, 21, 22, 23 (x64) # - Centos 7 (x64: on DigitalOcean droplet) # - CentOS 7 Minimal install in a Hyper-V VM + # - CentOS 6 (EPEL must be installed manually) if type dnf 2>/dev/null then @@ -228,54 +276,62 @@ BootstrapRpmCommon() { exit 1 fi + pkgs=" + gcc + dialog + augeas-libs + openssl + openssl-devel + libffi-devel + redhat-rpm-config + ca-certificates + " + # Some distros and older versions of current distros use a "python27" # instead of "python" naming convention. Try both conventions. - if ! $SUDO $tool install -y \ - python \ - python-devel \ - python-virtualenv \ - python-tools \ - python-pip - then - if ! $SUDO $tool install -y \ - python27 \ - python27-devel \ - python27-virtualenv \ - python27-tools \ - python27-pip - then - echo "Could not install Python dependencies. Aborting bootstrap!" - exit 1 - fi + if $SUDO $tool list python >/dev/null 2>&1; then + pkgs="$pkgs + python + python-devel + python-virtualenv + python-tools + python-pip + " + else + pkgs="$pkgs + python27 + python27-devel + python27-virtualenv + python27-tools + python27-pip + " fi - if ! $SUDO $tool install -y \ - gcc \ - dialog \ - augeas-libs \ - openssl \ - openssl-devel \ - libffi-devel \ - redhat-rpm-config \ - ca-certificates - then - echo "Could not install additional dependencies. Aborting bootstrap!" - exit 1 - fi - - if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then - if ! $SUDO $tool install -y mod_ssl - then - echo "Apache found, but mod_ssl could not be installed." - fi + pkgs="$pkgs + mod_ssl + " + fi + + if [ "$ASSUME_YES" = 1 ]; then + yes_flag="-y" + fi + + if ! $SUDO $tool install $yes_flag $pkgs; then + echo "Could not install OS dependencies. Aborting bootstrap!" + exit 1 fi } BootstrapSuseCommon() { # SLE12 don't have python-virtualenv - $SUDO zypper -nq in -l \ + if [ "$ASSUME_YES" = 1 ]; then + zypper_flags="-nq" + install_flags="-l" + fi + + $SUDO zypper $zypper_flags in $install_flags \ python \ python-devel \ python-virtualenv \ @@ -310,8 +366,12 @@ BootstrapArchCommon() { # pacman -T exits with 127 if there are missing dependencies missing=$($SUDO pacman -T $deps) || true + if [ "$ASSUME_YES" = 1 ]; then + noconfirm="--noconfirm" + fi + if [ "$missing" ]; then - $SUDO pacman -S --needed $missing + $SUDO pacman -S --needed $missing $noconfirm fi } @@ -426,7 +486,7 @@ Bootstrap() { elif grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon else - echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" @@ -446,7 +506,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then # --version output ran through grep due to python-cryptography DeprecationWarnings - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -465,8 +526,8 @@ if [ "$1" = "--le-auto-phase2" ]; then # There is no $ interpolation due to quotes on starting heredoc delimiter. # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" -# This is the flattened list of packages letsencrypt-auto installs. To generate -# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# This is the flattened list of packages certbot-auto installs. To generate +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. argparse==1.4.0 \ @@ -645,15 +706,21 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.5.0 \ - --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ - --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd -letsencrypt==0.5.0 \ - --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ - --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a -letsencrypt-apache==0.5.0 \ - --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ - --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -823,18 +890,30 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run letsencrypt..." - echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" - $SUDO "$VENV_BIN/letsencrypt" "$@" + echo "Requesting root privileges to run certbot..." + if [ -z "$SUDO_ENV" ] ; then + # SUDO is su wrapper / noop + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" + else + # sudo + echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + fi + else - # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. + # Phase 1: Upgrade certbot-auto if neceesary, then self-invoke. # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the letsencrypt - # package). Phase 2 checks the version of the locally installed letsencrypt. + # certbot-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi # If it looks like we've never bootstrapped before, bootstrap: Bootstrap fi @@ -953,7 +1032,7 @@ def verified_new_le_auto(get, tag, temp_dir): stderr=dev_null) except CalledProcessError as exc: raise ExpectedError("Couldn't verify signature of downloaded " - "letsencrypt-auto.", exc) + "certbot-auto.", exc) def main(): @@ -978,29 +1057,27 @@ if __name__ == '__main__': UNLIKELY_EOF # --------------------------------------------------------------------------- DeterminePythonVersion - REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` - if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then - echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then + echo "WARNING: unable to check for updates." + elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more # dependencies (curl, etc.), for better flow control, and for the option of # future Windows compatibility. "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" - # Install new copy of letsencrypt-auto. + # Install new copy of certbot-auto. # TODO: Deal with quotes in pathnames. - echo "Replacing letsencrypt-auto..." + echo "Replacing certbot-auto..." # Clone permissions with cp. chmod and chown don't have a --reference # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: - echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" - echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" # Using mv rather than cp leaves the old file descriptor pointing to the # original copy so the shell can continue to read it unmolested. mv across # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the # cp is unlikely to fail (esp. under sudo) if the rm doesn't. - echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" # TODO: Clean up temp dir safely, even if it has quotes in its path. rm -rf "$TEMP_DIR" diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc new file mode 100644 index 000000000..8b4f34c70 --- /dev/null +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1 + +iQEcBAABAgAGBQJXM9ZDAAoJEE0XyZXNl3XyzGkH/2KeR0jYxXKlvwfCkxU6hSC0 +eXcxZVQk59hCSvkNGE6Mj6rwQcyjSqmRp14MaJpq7NZADN6F+HWb6VB/Wq6moMQs +PJtthqwhF767Qg+Py9Hp6XmlKscjXB6AKCVxq5TBwEIOTtj0rhQRLF9/+GW6jFuf +kT6aUcDWNjOyWWUtp9vOVprDtegrltp0/2DNitlvPu263pKC+7I3GyLTq4fKP4EE +auZSAhFry9SNR3Usf2wD3kzhvLSrT3h9Yh5oA04oaX9H6e86EHwt6RJJRHpg8s6b +e0CBIIuaRJEmdiMUWlV/gAfH6M2PbG1wtJdxc0ThNEoWAjTsopr61BoHJ3cpCy4= +=+e7/ +-----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 8578feef2..8c6e6c486 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.6.0.dev0" +LE_AUTO_VERSION="0.6.0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -706,15 +706,21 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.5.0 \ - --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ - --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd -letsencrypt==0.5.0 \ - --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ - --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a -letsencrypt-apache==0.5.0 \ - --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ - --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 UNLIKELY_EOF # ------------------------------------------------------------------------- diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 36ab206aa47ee8d6348340e2e17e66ef840f4832..cb360f89ef5443af2c6bc157704a98d3cd68fcef 100644 GIT binary patch literal 256 zcmV+b0ssCk`Do-WYWlUAhmM)Jao($YX$9Q(cRVv8_jtP0o}fz5%~*%F!qFPG{FHTs zO#Q7Ny@?Hph=ylMIohpY9^{Zmm8Bm3`9U?K3CDJ~iXxyHmSNf`Uq7cCyf|^Mc+2uA z+A^~xvGqM}C5Xzw8cyZq9H=SydJxxr0AB GYGOMK)`FS< literal 256 zcmV+b0ssCNAlb`yp8ViHw_s6WQdeQ~rmeR1#f`~|;Lcvp1i>?dOn)2S?|UXU3-X4k zaZ%*t!KjDe7yUlErpaEi6!y=8HI`DRz)EuS7bCS?0nWEvmfo;6rg zd*P;)T`MMf6qh)Nq~anD`EcT+-{;e}X{!NGWzN5f-uFVW&D40h!-VgVXfn*mr{?;Y4vJd_;=@v>g=fCE0&HR8N!p!aAH z8#Erxi1sEj2Zdym%<&YjXJ9%;%Y|e9?#~icJ0k#V&W79xyG>~1A}+~ diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index 3a1ce34f1..f4ceae536 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -178,12 +178,18 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.5.0 \ - --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ - --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd -letsencrypt==0.5.0 \ - --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ - --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a -letsencrypt-apache==0.5.0 \ - --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ - --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index b94b7f69f..12578cc18 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0.dev0' +version = '0.6.0' # This package is a simple shim around certbot-nginx diff --git a/letsencrypt/setup.py b/letsencrypt/setup.py index 708c31f4b..d30146621 100644 --- a/letsencrypt/setup.py +++ b/letsencrypt/setup.py @@ -20,7 +20,7 @@ readme = read_file(os.path.join(here, 'README.rst')) install_requires = ['certbot'] -version = '0.6.0.dev0' +version = '0.6.0' setup( diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py index 8359d2766..8c302472a 100644 --- a/letshelp-certbot/setup.py +++ b/letshelp-certbot/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.6.0' install_requires = [ 'setuptools', # pkg_resources diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index 875c6fc92..bc4141513 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0.dev0' +version = '0.6.0' # This package is a simple shim around letshelp-certbot From c8cf0b460045c9853136d74772c4235817c1d1e4 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 11 May 2016 18:04:27 -0700 Subject: [PATCH 315/317] Bump version to 0.7.0 --- acme/setup.py | 2 +- certbot-apache/setup.py | 2 +- certbot-compatibility-test/setup.py | 2 +- certbot-nginx/setup.py | 2 +- certbot/__init__.py | 2 +- letsencrypt-apache/setup.py | 2 +- letsencrypt-auto-source/letsencrypt-auto | 2 +- letsencrypt-nginx/setup.py | 2 +- letsencrypt/setup.py | 2 +- letshelp-certbot/setup.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 20c7c7226..d38864dc1 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0' +version = '0.7.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 538a68139..56c6a451d 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0' +version = '0.7.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index ee2e128dd..8f9452c5a 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0' +version = '0.7.0.dev0' install_requires = [ 'certbot=={0}'.format(version), diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 634092fff..a74b93093 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0' +version = '0.7.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot/__init__.py b/certbot/__init__.py index 84022010b..85f370e7a 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.6.0' +__version__ = '0.7.0.dev0' diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 1718cc5b4..b94746150 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0' +version = '0.7.0.dev0' # This package is a simple shim around certbot-apache diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 8c6e6c486..b255a99a7 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.6.0" +LE_AUTO_VERSION="0.7.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 12578cc18..95ffd6cd8 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0' +version = '0.7.0.dev0' # This package is a simple shim around certbot-nginx diff --git a/letsencrypt/setup.py b/letsencrypt/setup.py index d30146621..7c974ea9b 100644 --- a/letsencrypt/setup.py +++ b/letsencrypt/setup.py @@ -20,7 +20,7 @@ readme = read_file(os.path.join(here, 'README.rst')) install_requires = ['certbot'] -version = '0.6.0' +version = '0.7.0.dev0' setup( diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py index 8c302472a..b616da688 100644 --- a/letshelp-certbot/setup.py +++ b/letshelp-certbot/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0' +version = '0.7.0.dev0' install_requires = [ 'setuptools', # pkg_resources diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index bc4141513..10380c49b 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0' +version = '0.7.0.dev0' # This package is a simple shim around letshelp-certbot From f53f4dd4913b1dbfd82d34e05927d91cf228d56f Mon Sep 17 00:00:00 2001 From: Noah Swartz Date: Thu, 12 May 2016 09:30:45 -0700 Subject: [PATCH 316/317] fix docs copyright --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index b2b31ac5d..e387e1eae 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -65,7 +65,7 @@ master_doc = 'index' # General information about the project. project = u'Certbot' -copyright = u'2014-2016, Certbot Project' +copyright = u'2014-2016 - The Certbot software and documentation are licensed under the Apache 2.0 license as described at https://eff.org/cb-license ' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 0d5c1638e72d6a9e347cbcdfd1baf58df83fab6e Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 12 May 2016 10:06:11 -0700 Subject: [PATCH 317/317] add certbot-auto and PGP sig --- certbot-auto | 1011 ++++++++++++++++++++++ letsencrypt-auto-source/certbot-auto.asc | 11 + 2 files changed, 1022 insertions(+) create mode 100755 certbot-auto create mode 100644 letsencrypt-auto-source/certbot-auto.asc diff --git a/certbot-auto b/certbot-auto new file mode 100755 index 000000000..942fd8ea2 --- /dev/null +++ b/certbot-auto @@ -0,0 +1,1011 @@ +#!/bin/sh +# +# Download and run the latest release version of the Let's Encrypt client. +# +# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING +# +# IF YOU WANT TO EDIT IT LOCALLY, *ALWAYS* RUN YOUR COPY WITH THE +# "--no-self-upgrade" FLAG +# +# IF YOU WANT TO SEND PULL REQUESTS, THE REAL SOURCE FOR THIS FILE IS +# letsencrypt-auto-source/letsencrypt-auto.template AND +# letsencrypt-auto-source/pieces/bootstrappers/* + +set -e # Work even if somebody does "sh thisscript.sh". + +# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script, +# if you want to change where the virtual environment will be installed +XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} +VENV_NAME="letsencrypt" +VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} +VENV_BIN="$VENV_PATH/bin" +LE_AUTO_VERSION="0.5.0" + +# This script takes the same arguments as the main letsencrypt program, but it +# additionally responds to --verbose (more output) and --debug (allow support +# for experimental platforms) +for arg in "$@" ; do + case "$arg" in + --debug) + DEBUG=1;; + --os-packages-only) + OS_PACKAGES_ONLY=1;; + --no-self-upgrade) + # Do not upgrade this script (also prevents client upgrades, because each + # copy of the script pins a hash of the python client) + NO_SELF_UPGRADE=1;; + --verbose) + VERBOSE=1;; + [!-]*|-*[!v]*|-) + # Anything that isn't -v, -vv, etc.: that is, anything that does not + # start with a -, contains anything that's not a v, or is just "-" + ;; + *) # -v+ remains. + VERBOSE=1;; + esac +done + +# letsencrypt-auto needs root access to bootstrap OS dependencies, and +# letsencrypt itself needs root access for almost all modes of operation +# The "normal" case is that sudo is used for the steps that need root, but +# this script *can* be run as root (not recommended), or fall back to using +# `su` +if test "`id -u`" -ne "0" ; then + if command -v sudo 1>/dev/null 2>&1; then + SUDO=sudo + else + echo \"sudo\" is not available, will use \"su\" for installation steps... + # Because the parameters in `su -c` has to be a string, + # we need properly escape it + su_sudo() { + args="" + # This `while` loop iterates over all parameters given to this function. + # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string + # will be wrapped in a pair of `'`, then appended to `$args` string + # For example, `echo "It's only 1\$\!"` will be escaped to: + # 'echo' 'It'"'"'s only 1$!' + # │ │└┼┘│ + # │ │ │ └── `'s only 1$!'` the literal string + # │ │ └── `\"'\"` is a single quote (as a string) + # │ └── `'It'`, to be concatenated with the strings following it + # └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself + while [ $# -ne 0 ]; do + args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' " + shift + done + su root -c "$args" + } + SUDO=su_sudo + fi +else + SUDO= +fi + +ExperimentalBootstrap() { + # Arguments: Platform name, bootstrap function name + if [ "$DEBUG" = 1 ]; then + if [ "$2" != "" ]; then + echo "Bootstrapping dependencies via $1..." + $2 + fi + else + echo "WARNING: $1 support is very experimental at present..." + echo "if you would like to work on improving it, please ensure you have backups" + echo "and then run this script again with the --debug flag!" + exit 1 + fi +} + +DeterminePythonVersion() { + for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do + # Break (while keeping the LE_PYTHON value) if found. + command -v "$LE_PYTHON" > /dev/null && break + done + if [ "$?" != "0" ]; then + echo "Cannot find any Pythons; please install one!" + exit 1 + fi + export LE_PYTHON + + PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + if [ "$PYVER" -lt 26 ]; then + echo "You have an ancient version of Python entombed in your operating system..." + echo "This isn't going to work; you'll need at least version 2.6." + exit 1 + fi +} + +BootstrapDebCommon() { + # 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) + + $SUDO apt-get update || echo apt-get update hit problems but continuing anyway... + + # virtualenv binary can be found in different packages depending on + # distro version (#346) + + virtualenv= + if apt-cache show virtualenv > /dev/null 2>&1; then + virtualenv="virtualenv" + fi + + if apt-cache show python-virtualenv > /dev/null 2>&1; then + virtualenv="$virtualenv python-virtualenv" + fi + + augeas_pkg="libaugeas0 augeas-lenses" + AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + + AddBackportRepo() { + # ARGS: + BACKPORT_NAME="$1" + BACKPORT_SOURCELINE="$2" + if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then + # This can theoretically error if sources.list.d is empty, but in that case we don't care. + if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s + if echo $BACKPORT_NAME | grep -q wheezy ; then + /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + fi + + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update + fi + fi + $SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + + } + + + if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then + if lsb_release -a | grep -q wheezy ; then + AddBackportRepo wheezy-backports "deb http://http.debian.net/debian wheezy-backports main" + elif lsb_release -a | grep -q precise ; then + # XXX add ARM case + AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" + else + echo "No libaugeas0 version is available that's new enough to run the" + echo "Let's Encrypt apache plugin..." + fi + # XXX add a case for ubuntu PPAs + fi + + $SUDO apt-get install -y --no-install-recommends \ + python \ + python-dev \ + $virtualenv \ + gcc \ + dialog \ + $augeas_pkg \ + libssl-dev \ + libffi-dev \ + ca-certificates \ + + + + if ! command -v virtualenv > /dev/null ; then + echo Failed to install a working \"virtualenv\" command, exiting + exit 1 + fi +} + +BootstrapRpmCommon() { + # Tested with: + # - Fedora 22, 23 (x64) + # - Centos 7 (x64: on DigitalOcean droplet) + # - CentOS 7 Minimal install in a Hyper-V VM + + if type dnf 2>/dev/null + then + tool=dnf + elif type yum 2>/dev/null + then + tool=yum + + else + echo "Neither yum nor dnf found. Aborting bootstrap!" + exit 1 + fi + + # Some distros and older versions of current distros use a "python27" + # instead of "python" naming convention. Try both conventions. + if ! $SUDO $tool install -y \ + python \ + python-devel \ + python-virtualenv \ + python-tools \ + python-pip + then + if ! $SUDO $tool install -y \ + python27 \ + python27-devel \ + python27-virtualenv \ + python27-tools \ + python27-pip + then + echo "Could not install Python dependencies. Aborting bootstrap!" + exit 1 + fi + fi + + if ! $SUDO $tool install -y \ + gcc \ + dialog \ + augeas-libs \ + openssl \ + openssl-devel \ + libffi-devel \ + redhat-rpm-config \ + ca-certificates + then + echo "Could not install additional dependencies. Aborting bootstrap!" + exit 1 + fi + + + if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then + if ! $SUDO $tool install -y mod_ssl + then + echo "Apache found, but mod_ssl could not be installed." + fi + fi +} + +BootstrapSuseCommon() { + # SLE12 don't have python-virtualenv + + $SUDO zypper -nq in -l \ + python \ + python-devel \ + python-virtualenv \ + gcc \ + dialog \ + augeas-lenses \ + libopenssl-devel \ + libffi-devel \ + ca-certificates +} + +BootstrapArchCommon() { + # Tested with: + # - ArchLinux (x86_64) + # + # "python-virtualenv" is Python3, but "python2-virtualenv" provides + # only "virtualenv2" binary, not "virtualenv" necessary in + # ./tools/_venv_common.sh + + deps=" + python2 + python-virtualenv + gcc + dialog + augeas + openssl + libffi + ca-certificates + pkg-config + " + + # pacman -T exits with 127 if there are missing dependencies + missing=$($SUDO pacman -T $deps) || true + + if [ "$missing" ]; then + $SUDO pacman -S --needed $missing + fi +} + +BootstrapGentooCommon() { + PACKAGES=" + 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) + $SUDO cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x + ;; + (pkgcore) + $SUDO pmerge --noreplace --oneshot $PACKAGES + ;; + (portage|*) + $SUDO emerge --noreplace --oneshot $PACKAGES + ;; + esac +} + +BootstrapFreeBsd() { + $SUDO pkg install -Ay \ + python \ + py27-virtualenv \ + augeas \ + libffi +} + +BootstrapMac() { + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" + fi + + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python + fi + + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib + fi + + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python + fi + + if ! hash virtualenv 2>/dev/null; then + echo "virtualenv not installed." + echo "Installing with pip..." + pip install virtualenv + fi +} + + +# Install required OS packages: +Bootstrap() { + if [ -f /etc/debian_version ]; then + echo "Bootstrapping dependencies for Debian-based OSes..." + BootstrapDebCommon + elif [ -f /etc/redhat-release ]; then + echo "Bootstrapping dependencies for RedHat-based OSes..." + BootstrapRpmCommon + elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + echo "Bootstrapping dependencies for openSUSE-based OSes..." + BootstrapSuseCommon + elif [ -f /etc/arch-release ]; then + if [ "$DEBUG" = 1 ]; then + echo "Bootstrapping dependencies for Archlinux..." + BootstrapArchCommon + else + echo "Please use pacman to install letsencrypt packages:" + echo "# pacman -S letsencrypt letsencrypt-apache" + echo + echo "If you would like to use the virtualenv way, please run the script again with the" + echo "--debug flag." + exit 1 + fi + elif [ -f /etc/manjaro-release ]; then + ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon + elif [ -f /etc/gentoo-release ]; then + ExperimentalBootstrap "Gentoo" BootstrapGentooCommon + elif uname | grep -iq FreeBSD ; then + ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd + elif uname | grep -iq Darwin ; then + ExperimentalBootstrap "Mac OS X" BootstrapMac + elif grep -iq "Amazon Linux" /etc/issue ; then + ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon + 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 pip install manually." + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "for more info." + fi +} + +TempDir() { + mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || OS X +} + + + +if [ "$1" = "--le-auto-phase2" ]; then + # Phase 2: Create venv, install LE, and run. + + shift 1 # the --le-auto-phase2 arg + if [ -f "$VENV_BIN/letsencrypt" ]; then + # --version output ran through grep due to python-cryptography DeprecationWarnings + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) + else + INSTALLED_VERSION="none" + fi + if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then + echo "Creating virtual environment..." + DeterminePythonVersion + rm -rf "$VENV_PATH" + if [ "$VERBOSE" = 1 ]; then + virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" + else + virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" > /dev/null + fi + + echo "Installing Python packages..." + TEMP_DIR=$(TempDir) + # There is no $ interpolation due to quotes on starting heredoc delimiter. + # ------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" +# This is the flattened list of packages letsencrypt-auto installs. To generate +# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# then use `hashin` or a more secure method to gather the hashes. + +argparse==1.4.0 \ + --hash=sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314 \ + --hash=sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4 + +# This comes before cffi because cffi will otherwise install an unchecked +# version via setup_requires. +pycparser==2.14 \ + --hash=sha256:7959b4a74abdc27b312fed1c21e6caf9309ce0b29ea86b591fd2e99ecdf27f73 + +cffi==1.4.2 \ + --hash=sha256:53c1c9ddb30431513eb7f3cdef0a3e06b0f1252188aaa7744af0f5a4cd45dbaf \ + --hash=sha256:a568f49dfca12a8d9f370187257efc58a38109e1eee714d928561d7a018a64f8 \ + --hash=sha256:809c6ca8cfbcaeebfbd432b4576001b40d38ff2463773cb57577d75e1a020bc3 \ + --hash=sha256:86cdca2cd9cba41422230390df17dfeaa9f344a911e3975c8be9da57b35548e9 \ + --hash=sha256:24b13db84aec385ca23c7b8ded83ef8bb4177bc181d14758f9f975be5d020d86 \ + --hash=sha256:969aeffd7c0e097f6be1efd682c156ae226591a0793a94b6c2d5e4293f4c8d4e \ + --hash=sha256:000f358d4b0fa249feaab9c1ce7d5b2fe7e02e7bdf6806c26418505fc685e268 \ + --hash=sha256:a9d86f460bbd8358a2d513ad779e3f3fc878e3b93a00b5002faebf616ffe6b9c \ + --hash=sha256:3127b3ab33eb23ccac071f9a0802748e5cf7c5cbcd02482bb063e35b41dbb0b0 \ + --hash=sha256:e2b2d42236469a40224d39e7b6c60575f388b2f423f354c7ee90a5b7f58c8065 \ + --hash=sha256:8c2dccafee89b1b424b0bec6ad2dd9622c949d2024e929f5da1ed801eac75f1d \ + --hash=sha256:a4de7a4d11aed488bab4fb14f4988587a829bece5a20433f780d6e33b08083cb \ + --hash=sha256:5ca8fe30425265a49274e4b0213a1bc98f4b13449ae5e96f984771e5d83e58c1 \ + --hash=sha256:a4fd38802f59e714eba81a024f62db710b27dbe27a7ea12e911537327aa84d30 \ + --hash=sha256:86cd6912bbc83e9405d4a73cd7f4b4ee8353652d2dbc7c820106ed5b4d1bab3a \ + --hash=sha256:8f1d177d364ea35900415ae24ca3e471be3d5334ed0419294068c49f45913998 +ConfigArgParse==0.10.0 \ + --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +configobj==5.0.6 \ + --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 +cryptography==1.2.3 \ + --hash=sha256:031938f73a5c5eb3e809e18ff7caeb6865351871417be6050cb8c86a9a202b9a \ + --hash=sha256:a179a38d50f8d68b491d7a313db78f8cabe290842cecddddc7b34d408e59db0a \ + --hash=sha256:906c88b2aadcf99cfabb24098263d1bf65ab0c8688acde10dae1f09d865920f1 \ + --hash=sha256:6e706c5c6088770b1d1b634e959e21963e315b0255f5f4777125ad3d54082977 \ + --hash=sha256:f5ebf8e31c48f8707921dca0e994de77813a9c9b9bf03c119c5ddf97bdcffe73 \ + --hash=sha256:c7b89e42288cc7fbee3812e99ef5c744f22452e11d6822f6807afc6d6b3be83e \ + --hash=sha256:8408d29865947109d8b68f1837a7cde1aa4dc86e0f79ca3ba58c0c44e443d6a5 \ + --hash=sha256:c7e76cf3c3d925dd31fa238cfb806cffba718c0f08707d77a538768477969956 \ + --hash=sha256:7d8de35380f31702758b7753bb5c40723832c73006dedb2f9099bf61a37f7287 \ + --hash=sha256:5edbee71fae5469ee83fe0a37866b9398c8ce3a46325c24fcedfbf097bb48a19 \ + --hash=sha256:594edafe4801c13bdc1cc305e7704a90c19617e95936f6ab457ee4ffe000ba50 \ + --hash=sha256:b7fdb16a0a7f481be42da744bfe1ea2163025de21f90f2c688a316f3c354da9c \ + --hash=sha256:207b8bf0fe0907336df38b733b487521cf9e138189aba9234ad54fe545dd0db8 \ + --hash=sha256:509a2f05386270cf783993c90d49ffefb3dd62aee45bf1ea8ce3d2cde7271c21 \ + --hash=sha256:ac69b65dd1af0179ede40c9f15788c88f73e628ea6c0519de3838e279bb388c6 \ + --hash=sha256:8df6fad6c6ae12fd7004ea29357f0a2b4d3774eaeca7656530d08d2d90cd41aa \ + --hash=sha256:0b8b96dd81cc1533a04f30382c0fe21c1972e189f794d0c4261a18cec08fd9b5 \ + --hash=sha256:cae8fca1883f23c50ea78d89de6fe4fefdb4cea83177760f47177559414ded93 \ + --hash=sha256:1a471ca576a9cdce1b1cd9f3a22b1d09ee44d46862037557de17919c0db44425 \ + --hash=sha256:8ec4e8e3d453b3a1b63b5f57737a434dcf1ee4a2f26f6ff7c5a37c3f679104d2 \ + --hash=sha256:8eb11c77dd8e73f48df6b2f7a7e16173fe0fe8fdfe266232832e88477e08454e +enum34==1.1.2 \ + --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ + --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 +funcsigs==0.4 \ + --hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \ + --hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033 +idna==2.0 \ + --hash=sha256:9b2fc50bd3c4ba306b9651b69411ef22026d4d8335b93afc2214cef1246ce707 \ + --hash=sha256:16199aad938b290f5be1057c0e1efc6546229391c23cea61ca940c115f7d3d3b +ipaddress==1.0.16 \ + --hash=sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5 \ + --hash=sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0 +linecache2==1.0.0 \ + --hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \ + --hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c +ndg-httpsclient==0.4.0 \ + --hash=sha256:e8c155fdebd9c4bcb0810b4ed01ae1987554b1ee034dd7532d7b8fdae38a6274 +ordereddict==1.1 \ + --hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f +parsedatetime==2.1 \ + --hash=sha256:ce9d422165cf6e963905cd5f74f274ebf7cc98c941916169178ef93f0e557838 \ + --hash=sha256:17c578775520c99131634e09cfca5a05ea9e1bd2a05cd06967ebece10df7af2d +pbr==1.8.1 \ + --hash=sha256:46c8db75ae75a056bd1cc07fa21734fe2e603d11a07833ecc1eeb74c35c72e0c \ + --hash=sha256:e2127626a91e6c885db89668976db31020f0af2da728924b56480fc7ccf09649 +psutil==3.3.0 \ + --hash=sha256:584f0b29fcc5d523b433cb8918b2fc74d67e30ee0b44a95baf031528f424619f \ + --hash=sha256:28ca0b6e9d99aa8dc286e8747a4471362b69812a25291de29b6a8d70a1545a0d \ + --hash=sha256:167ad5fff52a672c4ddc1c1a0b25146d6813ebb08a9aab0a3ac45f8a5b669c3b \ + --hash=sha256:e6dea6173a988727bb223d3497349ad5cdef5c0b282eff2d83e5f9065c53f85f \ + --hash=sha256:2af5e0a4aad66049955d0734aa4e3dc8caa17a9eaf8b4c1a27a5f1ee6e40f6fc \ + --hash=sha256:d9884dc0dc2e55e2448e495778dc9899c1c8bf37aeb2f434c1bea74af93c2683 \ + --hash=sha256:e27c2fe6dfcc8738be3d2c5a022f785eb72971057e1a9e1e34fba73bce8a71a6 \ + --hash=sha256:65afd6fecc8f3aed09ee4be63583bc8eb472f06ceaa4fe24c4d1d5a1a3c0e13f \ + --hash=sha256:ba1c558fbfcdf94515c2394b1155c1dc56e2bc2a9c17d30349827c9ed8a67e46 \ + --hash=sha256:ba95ea0022dcb64d36f0c1335c0605fae35bdf3e0fea8d92f5d0f6456a35e55b \ + --hash=sha256:421b6591d16b509aaa8d8c15821d66bb94cb4a8dc4385cad5c51b85d4a096d85 \ + --hash=sha256:326b305cbdb6f94dafbfe2c26b11da88b0ab07b8a07f8188ab9d75ff0c6e841a \ + --hash=sha256:9aede5b2b6fe46b3748ea8e5214443890d1634027bef3d33b7dad16556830278 \ + --hash=sha256:73bed1db894d1aa9c3c7e611d302cdeab7ae8a0dc0eeaf76727878db1ac5cd87 \ + --hash=sha256:935b5dd6d558af512f42501a7c08f41d7aff139af1bb3959daa3abb859234d6c \ + --hash=sha256:4ca0111cf157dcc0f2f69a323c5b5478718d68d45fc9435d84be0ec0f186215b \ + --hash=sha256:b6f13c95398a3fcf0226c4dcfa448560ba5865259cd96ec2810658651e932189 \ + --hash=sha256:ee6be30d1635bbdea4c4325d507dc8a0dbbde7e1c198bd62ddb9f43198b9e214 \ + --hash=sha256:dfa786858c268d7fbbe1b6175e001ec02738d7cfae0a7ce77bf9b651af676729 \ + --hash=sha256:aa77f9de72af9c16cc288cd4a24cf58824388f57d7a81e400c4616457629870e \ + --hash=sha256:f500093357d04da8140d87932cac2e54ef592a54ca8a743abb2850f60c2c22eb +pyasn1==0.1.9 \ + --hash=sha256:61f9d99e3cef65feb1bfe3a2eef7a93eb93819d345bf54bcd42f4e63d5204dae \ + --hash=sha256:1802a6dd32045e472a419db1441aecab469d33e0d2749e192abdec52101724af \ + --hash=sha256:35025cd9422c96504912f04e2f15fe79390a8597b430c2ca5d0534cf9309ffa0 \ + --hash=sha256:2f96ed5a0c329ca16230b326ca12b7461ec8f65e0be3e4f997516f36bf82a345 \ + --hash=sha256:28fee44217991cfad9e6a0b9f7e3f26041e21ebc96629e94e585ccd05d49fa65 \ + --hash=sha256:326e7a854a17fab07691204747695f8f692d674588a355c441fb14f660bf4e68 \ + --hash=sha256:cda5a90485709ca6795c86056c3e5fe7266028b05e53f1d527fdf93a6365a6b8 \ + --hash=sha256:0cb2a14742b543fdd68f931a14ce3829186ed2b1b2267a06787388c96b2dd9be \ + --hash=sha256:5191ff6b9126d2c039dd87f8ff025bed274baf07fa78afa46f556b1ad7265d6e \ + --hash=sha256:8323e03637b2d072cc7041300bac6ec448c3c28950ab40376036788e9a1af629 \ + --hash=sha256:853cacd96d1f701ddd67aa03ecc05f51890135b7262e922710112f12a2ed2a7f +pyOpenSSL==0.15.1 \ + --hash=sha256:88e45e6bb25dfed272a1ef2e728461d44b634c2cd689e989b6e56a349c5a3ae5 \ + --hash=sha256:f0a26070d6db0881de8bcc7846934b7c3c930d8f9c79d45883ee48984bc0d672 +pyRFC3339==1.0 \ + --hash=sha256:eea31835c56e2096af4363a5745a784878a61d043e247d3a6d6a0a32a9741f56 \ + --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 +python-augeas==0.5.0 \ + --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 +python2-pythondialog==3.3.0 \ + --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ + --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa +pytz==2015.7 \ + --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ + --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ + --hash=sha256:ead4aefa7007249e05e51b01095719d5a8dd95760089f5730aac5698b1932918 \ + --hash=sha256:3cca0df08bd0ed98432390494ce3ded003f5e661aa460be7a734bffe35983605 \ + --hash=sha256:3ede470d3d17ba3c07638dfa0d10452bc1b6e5ad326127a65ba77e6aaeb11bec \ + --hash=sha256:68c47964f7186eec306b13629627722b9079cd4447ed9e5ecaecd4eac84ca734 \ + --hash=sha256:dd5d3991950aae40a6c81de1578942e73d629808cefc51d12cd157980e6cfc18 \ + --hash=sha256:a77c52062c07eb7c7b30545dbc73e32995b7e117eea750317b5cb5c7a4618f14 \ + --hash=sha256:81af9aec4bc960a9a0127c488f18772dae4634689233f06f65443e7b11ebeb51 \ + --hash=sha256:e079b1dadc5c06246cc1bb6fe1b23a50b1d1173f2edd5104efd40bb73a28f406 \ + --hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \ + --hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \ + --hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3 +requests==2.9.1 \ + --hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \ + --hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f +six==1.10.0 \ + --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \ + --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a +traceback2==1.4.0 \ + --hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23 \ + --hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 +unittest2==1.1.0 \ + --hash=sha256:13f77d0875db6d9b435e1d4f41e74ad4cc2eb6e1d5c824996092b3430f088bb8 \ + --hash=sha256:22882a0e418c284e1f718a822b3b022944d53d2d908e1690b319a9d3eb2c0579 +zope.component==4.2.2 \ + --hash=sha256:282c112b55dd8e3c869a3571f86767c150ab1284a9ace2bdec226c592acaf81a +zope.event==4.1.0 \ + --hash=sha256:dc7a59a2fd91730d3793131a5d261b29e93ec4e2a97f1bc487ce8defee2fe786 +zope.interface==4.1.3 \ + --hash=sha256:f07b631f7a601cd8cbd3332d54f43142c7088a83299f859356f08d1d4d4259b3 \ + --hash=sha256:de5cca083b9439d8002fb76bbe6b4998c5a5a721fab25b84298967f002df4c94 \ + --hash=sha256:6788416f7ea7f5b8a97be94825377aa25e8bdc73463e07baaf9858b29e737077 \ + --hash=sha256:6f3230f7254518201e5a3708cbb2de98c848304f06e3ded8bfb39e5825cba2e1 \ + --hash=sha256:5fa575a5240f04200c3088427d0d4b7b737f6e9018818a51d8d0f927a6a2517a \ + --hash=sha256:522194ad6a545735edd75c8a83f48d65d1af064e432a7d320d64f56bafc12e99 \ + --hash=sha256:e8c7b2d40943f71c99148c97f66caa7f5134147f57423f8db5b4825099ce9a09 \ + --hash=sha256:279024f0208601c3caa907c53876e37ad88625f7eaf1cb3842dbe360b2287017 \ + --hash=sha256:2e221a9eec7ccc58889a278ea13dcfed5ef939d80b07819a9a8b3cb1c681484f \ + --hash=sha256:69118965410ec86d44dc6b9017ee3ddbd582e0c0abeef62b3a19dbf6c8ad132b \ + --hash=sha256:d04df8686ec864d0cade8cf199f7f83aecd416109a20834d568f8310ded12dea \ + --hash=sha256:e75a947e15ee97e7e71e02ea302feb2fc62d3a2bb4668bf9dfbed43a506ac7e7 \ + --hash=sha256:4e45d22fb883222a5ab9f282a116fec5ee2e8d1a568ccff6a2d75bbd0eb6bcfc \ + --hash=sha256:bce9339bb3c7a55e0803b63d21c5839e8e479bc85c4adf42ae415b72f94facb2 \ + --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ + --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ + --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 +mock==1.0.1 \ + --hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \ + --hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc + +# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. + +acme==0.5.0 \ + --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ + --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd +letsencrypt==0.5.0 \ + --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ + --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a +letsencrypt-apache==0.5.0 \ + --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ + --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 + +UNLIKELY_EOF + # ------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" +#!/usr/bin/env python +"""A small script that can act as a trust root for installing pip 8 + +Embed this in your project, and your VCS checkout is all you have to trust. In +a post-peep era, this lets you claw your way to a hash-checking version of pip, +with which you can install the rest of your dependencies safely. All it assumes +is Python 2.6 or better and *some* version of pip already installed. If +anything goes wrong, it will exit with a non-zero status code. + +""" +# This is here so embedded copies are MIT-compliant: +# Copyright (c) 2016 Erik Rose +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +from __future__ import print_function +from hashlib import sha256 +from os.path import join +from pipes import quote +from shutil import rmtree +try: + from subprocess import check_output +except ImportError: + from subprocess import CalledProcessError, PIPE, Popen + + def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be ' + 'overridden.') + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return output +from sys import exit, version_info +from tempfile import mkdtemp +try: + from urllib2 import build_opener, HTTPHandler, HTTPSHandler +except ImportError: + from urllib.request import build_opener, HTTPHandler, HTTPSHandler +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse # 3.4 + + +__version__ = 1, 1, 1 + + +# wheel has a conditional dependency on argparse: +maybe_argparse = ( + [('https://pypi.python.org/packages/source/a/argparse/' + 'argparse-1.4.0.tar.gz', + '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] + if version_info < (2, 7, 0) else []) + + +PACKAGES = maybe_argparse + [ + # Pip has no dependencies, as it vendors everything: + ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', + '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + # This version of setuptools has only optional dependencies: + ('https://pypi.python.org/packages/source/s/setuptools/' + 'setuptools-20.2.2.tar.gz', + '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), + ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') +] + + +class HashError(Exception): + def __str__(self): + url, path, actual, expected = self.args + return ('{url} did not match the expected hash {expected}. Instead, ' + 'it was {actual}. The file (left at {path}) may have been ' + 'tampered with.'.format(**locals())) + + +def hashed_download(url, temp, digest): + """Download ``url`` to ``temp``, make sure it has the SHA-256 ``digest``, + and return its path.""" + # Based on pip 1.4.1's URLOpener but with cert verification removed. Python + # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert + # authenticity has only privacy (not arbitrary code execution) + # implications, since we're checking hashes. + def opener(): + opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) + return opener + + def read_chunks(response, chunk_size): + while True: + chunk = response.read(chunk_size) + if not chunk: + break + yield chunk + + response = opener().open(url) + path = join(temp, urlparse(url).path.split('/')[-1]) + actual_hash = sha256() + with open(path, 'wb') as file: + for chunk in read_chunks(response, 4096): + file.write(chunk) + actual_hash.update(chunk) + + actual_digest = actual_hash.hexdigest() + if actual_digest != digest: + raise HashError(url, path, actual_digest, digest) + return path + + +def main(): + temp = mkdtemp(prefix='pipstrap-') + try: + downloads = [hashed_download(url, temp, digest) + for url, digest in PACKAGES] + check_output('pip install --no-index --no-deps -U ' + + ' '.join(quote(d) for d in downloads), + shell=True) + except HashError as exc: + print(exc) + except Exception: + rmtree(temp) + raise + else: + rmtree(temp) + return 0 + return 1 + + +if __name__ == '__main__': + exit(main()) + +UNLIKELY_EOF + # ------------------------------------------------------------------------- + # Set PATH so pipstrap upgrades the right (v)env: + PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" + set +e + PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_STATUS=$? + set -e + rm -rf "$TEMP_DIR" + if [ "$PIP_STATUS" != 0 ]; then + # Report error. (Otherwise, be quiet.) + echo "Had a problem while installing Python packages:" + echo "$PIP_OUT" + rm -rf "$VENV_PATH" + exit 1 + fi + echo "Installation succeeded." + fi + echo "Requesting root privileges to run letsencrypt..." + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" +else + # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. + # + # Each phase checks the version of only the thing it is responsible for + # upgrading. Phase 1 checks the version of the latest release of + # letsencrypt-auto (which is always the same as that of the letsencrypt + # package). Phase 2 checks the version of the locally installed letsencrypt. + + if [ ! -f "$VENV_BIN/letsencrypt" ]; then + # If it looks like we've never bootstrapped before, bootstrap: + Bootstrap + fi + if [ "$OS_PACKAGES_ONLY" = 1 ]; then + echo "OS packages installed." + exit 0 + fi + + if [ "$NO_SELF_UPGRADE" != 1 ]; then + echo "Checking for new version..." + TEMP_DIR=$(TempDir) + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" +"""Do downloading and JSON parsing without additional dependencies. :: + + # Print latest released version of LE to stdout: + python fetch.py --latest-version + + # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm + # in, and make sure its signature verifies: + python fetch.py --le-auto-script v1.2.3 + +On failure, return non-zero. + +""" +from distutils.version import LooseVersion +from json import loads +from os import devnull, environ +from os.path import dirname, join +import re +from subprocess import check_call, CalledProcessError +from sys import argv, exit +from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError + +PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq +OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18 +xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp +9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij +n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH +cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+ +CQIDAQAB +-----END PUBLIC KEY----- +""") + +class ExpectedError(Exception): + """A novice-readable exception that also carries the original exception for + debugging""" + + +class HttpsGetter(object): + def __init__(self): + """Build an HTTPS opener.""" + # Based on pip 1.4.1's URLOpener + # This verifies certs on only Python >=2.7.9. + self._opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in self._opener.handlers: + if isinstance(handler, HTTPHandler): + self._opener.handlers.remove(handler) + + def get(self, url): + """Return the document contents pointed to by an HTTPS URL. + + If something goes wrong (404, timeout, etc.), raise ExpectedError. + + """ + try: + return self._opener.open(url).read() + except (HTTPError, IOError) as exc: + raise ExpectedError("Couldn't download %s." % url, exc) + + +def write(contents, dir, filename): + """Write something to a file in a certain directory.""" + with open(join(dir, filename), 'w') as file: + file.write(contents) + + +def latest_stable_version(get): + """Return the latest stable release of letsencrypt.""" + metadata = loads(get( + environ.get('LE_AUTO_JSON_URL', + 'https://pypi.python.org/pypi/letsencrypt/json'))) + # metadata['info']['version'] actually returns the latest of any kind of + # release release, contrary to https://wiki.python.org/moin/PyPIJSON. + # The regex is a sufficient regex for picking out prereleases for most + # packages, LE included. + return str(max(LooseVersion(r) for r + in metadata['releases'].iterkeys() + if re.match('^[0-9.]+$', r))) + + +def verified_new_le_auto(get, tag, temp_dir): + """Return the path to a verified, up-to-date letsencrypt-auto script. + + If the download's signature does not verify or something else goes wrong + with the verification process, raise ExpectedError. + + """ + le_auto_dir = environ.get( + 'LE_AUTO_DIR_TEMPLATE', + 'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/' + 'letsencrypt-auto-source/') % tag + write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto') + write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig') + write(PUBLIC_KEY, temp_dir, 'public_key.pem') + try: + with open(devnull, 'w') as dev_null: + check_call(['openssl', 'dgst', '-sha256', '-verify', + join(temp_dir, 'public_key.pem'), + '-signature', + join(temp_dir, 'letsencrypt-auto.sig'), + join(temp_dir, 'letsencrypt-auto')], + stdout=dev_null, + stderr=dev_null) + except CalledProcessError as exc: + raise ExpectedError("Couldn't verify signature of downloaded " + "letsencrypt-auto.", exc) + + +def main(): + get = HttpsGetter().get + flag = argv[1] + try: + if flag == '--latest-version': + print latest_stable_version(get) + elif flag == '--le-auto-script': + tag = argv[2] + verified_new_le_auto(get, tag, dirname(argv[0])) + except ExpectedError as exc: + print exc.args[0], exc.args[1] + return 1 + else: + return 0 + + +if __name__ == '__main__': + exit(main()) + +UNLIKELY_EOF + # --------------------------------------------------------------------------- + DeterminePythonVersion + REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` + if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + + # Now we drop into Python so we don't have to install even more + # dependencies (curl, etc.), for better flow control, and for the option of + # future Windows compatibility. + "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" + + # Install new copy of letsencrypt-auto. + # TODO: Deal with quotes in pathnames. + echo "Replacing letsencrypt-auto..." + # Clone permissions with cp. chmod and chown don't have a --reference + # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: + echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" + $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" + echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" + $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" + # Using mv rather than cp leaves the old file descriptor pointing to the + # original copy so the shell can continue to read it unmolested. mv across + # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the + # cp is unlikely to fail (esp. under sudo) if the rm doesn't. + echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" + $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" + # TODO: Clean up temp dir safely, even if it has quotes in its path. + rm -rf "$TEMP_DIR" + fi # A newer version is available. + fi # Self-upgrading is allowed. + + "$0" --le-auto-phase2 "$@" +fi diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc new file mode 100644 index 000000000..593e644ec --- /dev/null +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1 + +iQEcBAABAgAGBQJXNLdqAAoJEE0XyZXNl3Xyk3cH/1OxJ16VbX9PK4U60mVRPseA +wzaCFOCVVB3Ub9m6PMzOw6kV9KLXuiMgh1qj9j4AH5UdFwynv6WNe+pxO3deYO6O +DBhlW0ibDoIGLb2nvHDmBXfX+CE8ixvo2pp7ao/StV5MTHSoDHfMZbT7ql1xpx7U +XOaPfl8MlmIlii6QFrBNZpUjE03NE8xUpocZq53eXxZiCRHLbO9z4j1e6NkaTotj +4Q1o9ERdAc5S7czF9TFwPgQWXoVxiMjhUUpNjfVFFjFB6r76AdVzz5PG6icZ6icM +C3kD1N4gm0sggNkWwDOfNAgTanVa/pmIv/wi7tkm/jpCfkErttnq9AjEhichaSY= +=SJou +-----END PGP SIGNATURE-----