mirror of
https://github.com/certbot/certbot.git
synced 2026-06-04 14:26:10 -04:00
Merge pull request #6651 from certbot/update-test-everything2
First, this PR merges master into the test-everything branch to resolve test failures created by #6597 and #6647. Second, installing packages through Homebrew started failing. I fixed this by making use of the Homebrew options in .travis.yml which didn't exist when our current Homebrew scripts were written. A summary of the changes there are: I removed tests/travis-macos-setup.sh (which only existed in the test-everything branch). I switched the packages installed by Homebrew to only install what's actually needed by the test. I started being explicit about the Python version we wanted because Homebrew has flipped back and forth on what the python package refers to. We stop calling brew link python. I think this is OK because brew link <package> just makes the package available in your PATH but we can see this is already the case with Python because the tests are using 2.7.15 which is available through Homebrew but not macOS.
This commit is contained in:
commit
7277c8b0cb
112 changed files with 1888 additions and 771 deletions
15
.travis.yml
15
.travis.yml
|
|
@ -12,10 +12,10 @@ before_install:
|
|||
- cp .travis.yml /tmp/travis.yml
|
||||
- git pull origin master --strategy=recursive --strategy-option=theirs --no-edit
|
||||
- if ! git diff .travis.yml /tmp/travis.yml ; then echo "Please merge master into test-everything"; exit 1; fi
|
||||
- '[ "$TRAVIS_OS_NAME" != osx ] || tests/travis-macos-setup.sh'
|
||||
|
||||
before_script:
|
||||
- 'if [ $TRAVIS_OS_NAME = osx ] ; then ulimit -n 1024 ; fi'
|
||||
- export TOX_TESTENV_PASSENV=TRAVIS
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
|
@ -75,8 +75,9 @@ matrix:
|
|||
packages: # don't install nginx and apache
|
||||
- libaugeas0
|
||||
- python: "2.7"
|
||||
env: TOXENV=apacheconftest
|
||||
env: TOXENV=apacheconftest-with-pebble
|
||||
sudo: required
|
||||
services: docker
|
||||
- python: "3.4"
|
||||
env: TOXENV=py34 BOULDER_INTEGRATION=v1
|
||||
sudo: required
|
||||
|
|
@ -120,9 +121,19 @@ matrix:
|
|||
- language: generic
|
||||
env: TOXENV=py27
|
||||
os: osx
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- augeas
|
||||
- python2
|
||||
- language: generic
|
||||
env: TOXENV=py3
|
||||
os: osx
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- augeas
|
||||
- python3
|
||||
|
||||
|
||||
# Only build pushes to the master branch, PRs, and branches beginning with
|
||||
|
|
|
|||
94
CHANGELOG.md
94
CHANGELOG.md
|
|
@ -1,8 +1,8 @@
|
|||
# Certbot change log
|
||||
|
||||
Certbot adheres to [Semantic Versioning](http://semver.org/).
|
||||
Certbot adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## 0.29.0 - master
|
||||
## 0.31.0 - master
|
||||
|
||||
### Added
|
||||
|
||||
|
|
@ -14,14 +14,102 @@ Certbot adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
### Fixed
|
||||
|
||||
*
|
||||
* Fixed accessing josepy contents through acme.jose when the full acme.jose
|
||||
path is used.
|
||||
|
||||
Despite us having broken lockstep, we are continuing to release new versions of
|
||||
all Certbot components during releases for the time being, however, the only
|
||||
package with changes other than its version number was:
|
||||
|
||||
* acme
|
||||
|
||||
More details about these changes can be found on our GitHub repo.
|
||||
|
||||
## 0.30.0 - 2019-01-02
|
||||
|
||||
### Added
|
||||
|
||||
* Added the `update_account` subcommand for account management commands.
|
||||
|
||||
### Changed
|
||||
|
||||
* Copied account management functionality from the `register` subcommand
|
||||
to the `update_account` subcommand.
|
||||
* Marked usage `register --update-registration` for deprecation and
|
||||
removal in a future release.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Older modules in the josepy library can now be accessed through acme.jose
|
||||
like it could in previous versions of acme. This is only done to preserve
|
||||
backwards compatibility and support for doing this with new modules in josepy
|
||||
will not be added. Users of the acme library should switch to using josepy
|
||||
directly if they haven't done so already.
|
||||
|
||||
Despite us having broken lockstep, we are continuing to release new versions of
|
||||
all Certbot components during releases for the time being, however, the only
|
||||
package with changes other than its version number was:
|
||||
|
||||
* acme
|
||||
|
||||
More details about these changes can be found on our GitHub repo.
|
||||
|
||||
## 0.29.1 - 2018-12-05
|
||||
|
||||
### Added
|
||||
|
||||
*
|
||||
|
||||
### Changed
|
||||
|
||||
*
|
||||
|
||||
### Fixed
|
||||
|
||||
* The default work and log directories have been changed back to
|
||||
/var/lib/letsencrypt and /var/log/letsencrypt respectively.
|
||||
|
||||
Despite us having broken lockstep, we are continuing to release new versions of
|
||||
all Certbot components during releases for the time being, however, the only
|
||||
package with changes other than its version number was:
|
||||
|
||||
* certbot
|
||||
|
||||
More details about these changes can be found on our GitHub repo.
|
||||
|
||||
## 0.29.0 - 2018-12-05
|
||||
|
||||
### Added
|
||||
|
||||
* Noninteractive renewals with `certbot renew` (those not started from a
|
||||
terminal) now randomly sleep 1-480 seconds before beginning work in
|
||||
order to spread out load spikes on the server side.
|
||||
* Added External Account Binding support in cli and acme library.
|
||||
Command line arguments --eab-kid and --eab-hmac-key added.
|
||||
|
||||
### Changed
|
||||
|
||||
* Private key permissioning changes: Renewal preserves existing group mode
|
||||
& gid of previous private key material. Private keys for new
|
||||
lineages (i.e. new certs, not renewed) default to 0o600.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Update code and dependencies to clean up Resource and Deprecation Warnings.
|
||||
* Only depend on imgconverter extension for Sphinx >= 1.6
|
||||
|
||||
Despite us having broken lockstep, we are continuing to release new versions of
|
||||
all Certbot components during releases for the time being, however, the only
|
||||
package with changes other than its version number was:
|
||||
|
||||
* acme
|
||||
* certbot
|
||||
* certbot-apache
|
||||
* certbot-dns-cloudflare
|
||||
* certbot-dns-digitalocean
|
||||
* certbot-dns-google
|
||||
* certbot-nginx
|
||||
|
||||
More details about these changes can be found on our GitHub repo:
|
||||
https://github.com/certbot/certbot/milestone/62?closed=1
|
||||
|
||||
|
|
|
|||
|
|
@ -10,3 +10,18 @@ supported version: `draft-ietf-acme-01`_.
|
|||
https://github.com/ietf-wg-acme/acme/tree/draft-ietf-acme-acme-01
|
||||
|
||||
"""
|
||||
import sys
|
||||
|
||||
# This code exists to keep backwards compatibility with people using acme.jose
|
||||
# before it became the standalone josepy package.
|
||||
#
|
||||
# It is based on
|
||||
# https://github.com/requests/requests/blob/1278ecdf71a312dc2268f3bfc0aabfab3c006dcf/requests/packages.py
|
||||
|
||||
import josepy as jose
|
||||
|
||||
for mod in list(sys.modules):
|
||||
# This traversal is apparently necessary such that the identities are
|
||||
# preserved (acme.jose.* is josepy.*)
|
||||
if mod == 'josepy' or mod.startswith('josepy.'):
|
||||
sys.modules['acme.' + mod.replace('josepy', 'jose', 1)] = sys.modules[mod]
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ logger = logging.getLogger(__name__)
|
|||
# https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning
|
||||
if sys.version_info < (2, 7, 9): # pragma: no cover
|
||||
try:
|
||||
# pylint: disable=no-member
|
||||
requests.packages.urllib3.contrib.pyopenssl.inject_into_urllib3() # type: ignore
|
||||
except AttributeError:
|
||||
import urllib3.contrib.pyopenssl # pylint: disable=import-error
|
||||
|
|
@ -199,22 +200,6 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
|||
|
||||
return datetime.datetime.now() + datetime.timedelta(seconds=seconds)
|
||||
|
||||
def poll(self, authzr):
|
||||
"""Poll Authorization Resource for status.
|
||||
|
||||
:param authzr: Authorization Resource
|
||||
:type authzr: `.AuthorizationResource`
|
||||
|
||||
:returns: Updated Authorization Resource and HTTP response.
|
||||
|
||||
:rtype: (`.AuthorizationResource`, `requests.Response`)
|
||||
|
||||
"""
|
||||
response = self.net.get(authzr.uri)
|
||||
updated_authzr = self._authzr_from_response(
|
||||
response, authzr.body.identifier, authzr.uri)
|
||||
return updated_authzr, response
|
||||
|
||||
def _revoke(self, cert, rsn, url):
|
||||
"""Revoke certificate.
|
||||
|
||||
|
|
@ -236,6 +221,7 @@ class ClientBase(object): # pylint: disable=too-many-instance-attributes
|
|||
raise errors.ClientError(
|
||||
'Successful revocation must return HTTP OK status')
|
||||
|
||||
|
||||
class Client(ClientBase):
|
||||
"""ACME client for a v1 API.
|
||||
|
||||
|
|
@ -388,6 +374,22 @@ class Client(ClientBase):
|
|||
body=jose.ComparableX509(OpenSSL.crypto.load_certificate(
|
||||
OpenSSL.crypto.FILETYPE_ASN1, response.content)))
|
||||
|
||||
def poll(self, authzr):
|
||||
"""Poll Authorization Resource for status.
|
||||
|
||||
:param authzr: Authorization Resource
|
||||
:type authzr: `.AuthorizationResource`
|
||||
|
||||
:returns: Updated Authorization Resource and HTTP response.
|
||||
|
||||
:rtype: (`.AuthorizationResource`, `requests.Response`)
|
||||
|
||||
"""
|
||||
response = self.net.get(authzr.uri)
|
||||
updated_authzr = self._authzr_from_response(
|
||||
response, authzr.body.identifier, authzr.uri)
|
||||
return updated_authzr, response
|
||||
|
||||
def poll_and_request_issuance(
|
||||
self, csr, authzrs, mintime=5, max_attempts=10):
|
||||
"""Poll and request issuance.
|
||||
|
|
@ -651,13 +653,29 @@ class ClientV2(ClientBase):
|
|||
body = messages.Order.from_json(response.json())
|
||||
authorizations = []
|
||||
for url in body.authorizations:
|
||||
authorizations.append(self._authzr_from_response(self.net.get(url), uri=url))
|
||||
authorizations.append(self._authzr_from_response(self._post_as_get(url), uri=url))
|
||||
return messages.OrderResource(
|
||||
body=body,
|
||||
uri=response.headers.get('Location'),
|
||||
authorizations=authorizations,
|
||||
csr_pem=csr_pem)
|
||||
|
||||
def poll(self, authzr):
|
||||
"""Poll Authorization Resource for status.
|
||||
|
||||
:param authzr: Authorization Resource
|
||||
:type authzr: `.AuthorizationResource`
|
||||
|
||||
:returns: Updated Authorization Resource and HTTP response.
|
||||
|
||||
:rtype: (`.AuthorizationResource`, `requests.Response`)
|
||||
|
||||
"""
|
||||
response = self._post_as_get(authzr.uri)
|
||||
updated_authzr = self._authzr_from_response(
|
||||
response, authzr.body.identifier, authzr.uri)
|
||||
return updated_authzr, response
|
||||
|
||||
def poll_and_finalize(self, orderr, deadline=None):
|
||||
"""Poll authorizations and finalize the order.
|
||||
|
||||
|
|
@ -681,7 +699,7 @@ class ClientV2(ClientBase):
|
|||
responses = []
|
||||
for url in orderr.body.authorizations:
|
||||
while datetime.datetime.now() < deadline:
|
||||
authzr = self._authzr_from_response(self.net.get(url), uri=url)
|
||||
authzr = self._authzr_from_response(self._post_as_get(url), uri=url)
|
||||
if authzr.body.status != messages.STATUS_PENDING:
|
||||
responses.append(authzr)
|
||||
break
|
||||
|
|
@ -716,12 +734,12 @@ class ClientV2(ClientBase):
|
|||
self._post(orderr.body.finalize, wrapped_csr)
|
||||
while datetime.datetime.now() < deadline:
|
||||
time.sleep(1)
|
||||
response = self.net.get(orderr.uri)
|
||||
response = self._post_as_get(orderr.uri)
|
||||
body = messages.Order.from_json(response.json())
|
||||
if body.error is not None:
|
||||
raise errors.IssuanceError(body.error)
|
||||
if body.certificate is not None:
|
||||
certificate_response = self.net.get(body.certificate,
|
||||
certificate_response = self._post_as_get(body.certificate,
|
||||
content_type=DER_CONTENT_TYPE).text
|
||||
return orderr.update(body=body, fullchain_pem=certificate_response)
|
||||
raise errors.TimeoutError()
|
||||
|
|
@ -739,6 +757,39 @@ class ClientV2(ClientBase):
|
|||
"""
|
||||
return self._revoke(cert, rsn, self.directory['revokeCert'])
|
||||
|
||||
def external_account_required(self):
|
||||
"""Checks if ACME server requires External Account Binding authentication."""
|
||||
if hasattr(self.directory, 'meta') and self.directory.meta.external_account_required:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _post_as_get(self, *args, **kwargs):
|
||||
"""
|
||||
Send GET request using the POST-as-GET protocol if needed.
|
||||
The request will be first issued using POST-as-GET for ACME v2. If the ACME CA servers do
|
||||
not support this yet and return an error, request will be retried using GET.
|
||||
For ACME v1, only GET request will be tried, as POST-as-GET is not supported.
|
||||
:param args:
|
||||
:param kwargs:
|
||||
:return:
|
||||
"""
|
||||
if self.acme_version >= 2:
|
||||
# We add an empty payload for POST-as-GET requests
|
||||
new_args = args[:1] + (None,) + args[1:]
|
||||
try:
|
||||
return self._post(*new_args, **kwargs) # pylint: disable=star-args
|
||||
except messages.Error as error:
|
||||
if error.code == 'malformed':
|
||||
logger.debug('Error during a POST-as-GET request, '
|
||||
'your ACME CA may not support it:\n%s', error)
|
||||
logger.debug('Retrying request with GET.')
|
||||
else: # pragma: no cover
|
||||
raise
|
||||
|
||||
# If POST-as-GET is not supported yet, we use a GET instead.
|
||||
return self.net.get(*args, **kwargs)
|
||||
|
||||
|
||||
class BackwardsCompatibleClientV2(object):
|
||||
"""ACME client wrapper that tends towards V2-style calls, but
|
||||
|
|
@ -768,12 +819,7 @@ class BackwardsCompatibleClientV2(object):
|
|||
self.client = ClientV2(directory, net=net)
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in vars(self.client):
|
||||
return getattr(self.client, name)
|
||||
elif name in dir(ClientBase):
|
||||
return getattr(self.client, name)
|
||||
else:
|
||||
raise AttributeError()
|
||||
return getattr(self.client, name)
|
||||
|
||||
def new_account_and_tos(self, regr, check_tos_cb=None):
|
||||
"""Combined register and agree_tos for V1, new_account for V2
|
||||
|
|
@ -880,6 +926,15 @@ class BackwardsCompatibleClientV2(object):
|
|||
else:
|
||||
return 1
|
||||
|
||||
def external_account_required(self):
|
||||
"""Checks if the server requires an external account for ACMEv2 servers.
|
||||
|
||||
Always return False for ACMEv1 servers, as it doesn't use External Account Binding."""
|
||||
if self.acme_version == 1:
|
||||
return False
|
||||
else:
|
||||
return self.client.external_account_required()
|
||||
|
||||
|
||||
class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
||||
"""Wrapper around requests that signs POSTs for authentication.
|
||||
|
|
@ -943,7 +998,7 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
|
|||
:rtype: `josepy.JWS`
|
||||
|
||||
"""
|
||||
jobj = obj.json_dumps(indent=2).encode()
|
||||
jobj = obj.json_dumps(indent=2).encode() if obj else b''
|
||||
logger.debug('JWS payload:\n%s', jobj)
|
||||
kwargs = {
|
||||
"alg": self.alg,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Tests for acme.client."""
|
||||
# pylint: disable=too-many-lines
|
||||
import copy
|
||||
import datetime
|
||||
import json
|
||||
|
|
@ -283,6 +284,37 @@ class BackwardsCompatibleClientV2Test(ClientTestBase):
|
|||
client.update_registration(mock.sentinel.regr, None)
|
||||
mock_client().update_registration.assert_called_once_with(mock.sentinel.regr, None)
|
||||
|
||||
# newNonce present means it will pick acme_version 2
|
||||
def test_external_account_required_true(self):
|
||||
self.response.json.return_value = messages.Directory({
|
||||
'newNonce': 'http://letsencrypt-test.com/acme/new-nonce',
|
||||
'meta': messages.Directory.Meta(external_account_required=True),
|
||||
}).to_json()
|
||||
|
||||
client = self._init()
|
||||
|
||||
self.assertTrue(client.external_account_required())
|
||||
|
||||
# newNonce present means it will pick acme_version 2
|
||||
def test_external_account_required_false(self):
|
||||
self.response.json.return_value = messages.Directory({
|
||||
'newNonce': 'http://letsencrypt-test.com/acme/new-nonce',
|
||||
'meta': messages.Directory.Meta(external_account_required=False),
|
||||
}).to_json()
|
||||
|
||||
client = self._init()
|
||||
|
||||
self.assertFalse(client.external_account_required())
|
||||
|
||||
def test_external_account_required_false_v1(self):
|
||||
self.response.json.return_value = messages.Directory({
|
||||
'meta': messages.Directory.Meta(external_account_required=False),
|
||||
}).to_json()
|
||||
|
||||
client = self._init()
|
||||
|
||||
self.assertFalse(client.external_account_required())
|
||||
|
||||
|
||||
class ClientTest(ClientTestBase):
|
||||
"""Tests for acme.client.Client."""
|
||||
|
|
@ -665,7 +697,7 @@ class ClientTest(ClientTestBase):
|
|||
def test_revocation_payload(self):
|
||||
obj = messages.Revocation(certificate=self.certr.body, reason=self.rsn)
|
||||
self.assertTrue('reason' in obj.to_partial_json().keys())
|
||||
self.assertEquals(self.rsn, obj.to_partial_json()['reason'])
|
||||
self.assertEqual(self.rsn, obj.to_partial_json()['reason'])
|
||||
|
||||
def test_revoke_bad_status_raises_error(self):
|
||||
self.response.status_code = http_client.METHOD_NOT_ALLOWED
|
||||
|
|
@ -675,6 +707,7 @@ class ClientTest(ClientTestBase):
|
|||
self.certr,
|
||||
self.rsn)
|
||||
|
||||
|
||||
class ClientV2Test(ClientTestBase):
|
||||
"""Tests for acme.client.ClientV2."""
|
||||
|
||||
|
|
@ -730,9 +763,10 @@ class ClientV2Test(ClientTestBase):
|
|||
authz_response2 = self.response
|
||||
authz_response2.json.return_value = self.authz2.to_json()
|
||||
authz_response2.headers['Location'] = self.authzr2.uri
|
||||
self.net.get.side_effect = (authz_response, authz_response2)
|
||||
|
||||
self.assertEqual(self.client.new_order(CSR_SAN_PEM), self.orderr)
|
||||
with mock.patch('acme.client.ClientV2._post_as_get') as mock_post_as_get:
|
||||
mock_post_as_get.side_effect = (authz_response, authz_response2)
|
||||
self.assertEqual(self.client.new_order(CSR_SAN_PEM), self.orderr)
|
||||
|
||||
@mock.patch('acme.client.datetime')
|
||||
def test_poll_and_finalize(self, mock_datetime):
|
||||
|
|
@ -821,6 +855,47 @@ class ClientV2Test(ClientTestBase):
|
|||
self.response.json.return_value = self.regr.body.update(
|
||||
contact=()).to_json()
|
||||
|
||||
def test_external_account_required_true(self):
|
||||
self.client.directory = messages.Directory({
|
||||
'meta': messages.Directory.Meta(external_account_required=True)
|
||||
})
|
||||
|
||||
self.assertTrue(self.client.external_account_required())
|
||||
|
||||
def test_external_account_required_false(self):
|
||||
self.client.directory = messages.Directory({
|
||||
'meta': messages.Directory.Meta(external_account_required=False)
|
||||
})
|
||||
|
||||
self.assertFalse(self.client.external_account_required())
|
||||
|
||||
def test_external_account_required_default(self):
|
||||
self.assertFalse(self.client.external_account_required())
|
||||
|
||||
def test_post_as_get(self):
|
||||
with mock.patch('acme.client.ClientV2._authzr_from_response') as mock_client:
|
||||
mock_client.return_value = self.authzr2
|
||||
|
||||
self.client.poll(self.authzr2) # pylint: disable=protected-access
|
||||
|
||||
self.client.net.post.assert_called_once_with(
|
||||
self.authzr2.uri, None, acme_version=2,
|
||||
new_nonce_url='https://www.letsencrypt-demo.org/acme/new-nonce')
|
||||
self.client.net.get.assert_not_called()
|
||||
|
||||
class FakeError(messages.Error): # pylint: disable=too-many-ancestors
|
||||
"""Fake error to reproduce a malformed request ACME error"""
|
||||
def __init__(self): # pylint: disable=super-init-not-called
|
||||
pass
|
||||
@property
|
||||
def code(self):
|
||||
return 'malformed'
|
||||
self.client.net.post.side_effect = FakeError()
|
||||
|
||||
self.client.poll(self.authzr2) # pylint: disable=protected-access
|
||||
|
||||
self.client.net.get.assert_called_once_with(self.authzr2.uri)
|
||||
|
||||
|
||||
class MockJSONDeSerializable(jose.JSONDeSerializable):
|
||||
# pylint: disable=missing-docstring
|
||||
|
|
@ -876,7 +951,6 @@ class ClientNetworkTest(unittest.TestCase):
|
|||
self.assertEqual(jws.signature.combined.kid, u'acct-uri')
|
||||
self.assertEqual(jws.signature.combined.url, u'url')
|
||||
|
||||
|
||||
def test_check_response_not_ok_jobj_no_error(self):
|
||||
self.response.ok = False
|
||||
self.response.json.return_value = {}
|
||||
|
|
@ -1039,8 +1113,8 @@ class ClientNetworkTest(unittest.TestCase):
|
|||
|
||||
# Requests Library Exceptions
|
||||
except requests.exceptions.ConnectionError as z: #pragma: no cover
|
||||
self.assertTrue("('Connection aborted.', error(111, 'Connection refused'))"
|
||||
== str(z) or "[WinError 10061]" in str(z))
|
||||
self.assertTrue("'Connection aborted.'" in str(z) or "[WinError 10061]" in str(z))
|
||||
|
||||
|
||||
class ClientNetworkWithMockedResponseTest(unittest.TestCase):
|
||||
"""Tests for acme.client.ClientNetwork which mock out response."""
|
||||
|
|
|
|||
|
|
@ -209,8 +209,8 @@ class MakeCSRTest(unittest.TestCase):
|
|||
# have a get_extensions() method, so we skip this test if the method
|
||||
# isn't available.
|
||||
if hasattr(csr, 'get_extensions'):
|
||||
self.assertEquals(len(csr.get_extensions()), 1)
|
||||
self.assertEquals(csr.get_extensions()[0].get_data(),
|
||||
self.assertEqual(len(csr.get_extensions()), 1)
|
||||
self.assertEqual(csr.get_extensions()[0].get_data(),
|
||||
OpenSSL.crypto.X509Extension(
|
||||
b'subjectAltName',
|
||||
critical=False,
|
||||
|
|
@ -227,7 +227,7 @@ class MakeCSRTest(unittest.TestCase):
|
|||
# have a get_extensions() method, so we skip this test if the method
|
||||
# isn't available.
|
||||
if hasattr(csr, 'get_extensions'):
|
||||
self.assertEquals(len(csr.get_extensions()), 2)
|
||||
self.assertEqual(len(csr.get_extensions()), 2)
|
||||
# NOTE: Ideally we would filter by the TLS Feature OID, but
|
||||
# OpenSSL.crypto.X509Extension doesn't give us the extension's raw OID,
|
||||
# and the shortname field is just "UNDEF"
|
||||
|
|
|
|||
53
acme/acme/jose_test.py
Normal file
53
acme/acme/jose_test.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
"""Tests for acme.jose shim."""
|
||||
import importlib
|
||||
import unittest
|
||||
|
||||
class JoseTest(unittest.TestCase):
|
||||
"""Tests for acme.jose shim."""
|
||||
|
||||
def _test_it(self, submodule, attribute):
|
||||
if submodule:
|
||||
acme_jose_path = 'acme.jose.' + submodule
|
||||
josepy_path = 'josepy.' + submodule
|
||||
else:
|
||||
acme_jose_path = 'acme.jose'
|
||||
josepy_path = 'josepy'
|
||||
acme_jose_mod = importlib.import_module(acme_jose_path)
|
||||
josepy_mod = importlib.import_module(josepy_path)
|
||||
|
||||
self.assertIs(acme_jose_mod, josepy_mod)
|
||||
self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute))
|
||||
|
||||
# We use the imports below with eval, but pylint doesn't
|
||||
# understand that.
|
||||
# pylint: disable=eval-used,unused-variable
|
||||
import acme
|
||||
import josepy
|
||||
acme_jose_mod = eval(acme_jose_path)
|
||||
josepy_mod = eval(josepy_path)
|
||||
self.assertIs(acme_jose_mod, josepy_mod)
|
||||
self.assertIs(getattr(acme_jose_mod, attribute), getattr(josepy_mod, attribute))
|
||||
|
||||
def test_top_level(self):
|
||||
self._test_it('', 'RS512')
|
||||
|
||||
def test_submodules(self):
|
||||
# This test ensures that the modules in josepy that were
|
||||
# available at the time it was moved into its own package are
|
||||
# available under acme.jose. Backwards compatibility with new
|
||||
# modules or testing code is not maintained.
|
||||
mods_and_attrs = [('b64', 'b64decode',),
|
||||
('errors', 'Error',),
|
||||
('interfaces', 'JSONDeSerializable',),
|
||||
('json_util', 'Field',),
|
||||
('jwa', 'HS256',),
|
||||
('jwk', 'JWK',),
|
||||
('jws', 'JWS',),
|
||||
('util', 'ImmutableMap',),]
|
||||
|
||||
for mod, attr in mods_and_attrs:
|
||||
self._test_it(mod, attr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
"""ACME protocol messages."""
|
||||
import collections
|
||||
import six
|
||||
import json
|
||||
|
||||
import josepy as jose
|
||||
|
||||
|
|
@ -8,6 +9,7 @@ from acme import challenges
|
|||
from acme import errors
|
||||
from acme import fields
|
||||
from acme import util
|
||||
from acme import jws
|
||||
|
||||
OLD_ERROR_PREFIX = "urn:acme:error:"
|
||||
ERROR_PREFIX = "urn:ietf:params:acme:error:"
|
||||
|
|
@ -27,6 +29,7 @@ ERROR_CODES = {
|
|||
'tls': 'The server experienced a TLS error during domain verification',
|
||||
'unauthorized': 'The client lacks sufficient authorization',
|
||||
'unknownHost': 'The server could not resolve a domain name',
|
||||
'externalAccountRequired': 'The server requires external account binding',
|
||||
}
|
||||
|
||||
ERROR_TYPE_DESCRIPTIONS = dict(
|
||||
|
|
@ -176,6 +179,7 @@ class Directory(jose.JSONDeSerializable):
|
|||
_terms_of_service_v2 = jose.Field('termsOfService', omitempty=True)
|
||||
website = jose.Field('website', omitempty=True)
|
||||
caa_identities = jose.Field('caaIdentities', omitempty=True)
|
||||
external_account_required = jose.Field('externalAccountRequired', omitempty=True)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
kwargs = dict((self._internal_name(k), v) for k, v in kwargs.items())
|
||||
|
|
@ -258,6 +262,24 @@ class ResourceBody(jose.JSONObjectWithFields):
|
|||
"""ACME Resource Body."""
|
||||
|
||||
|
||||
class ExternalAccountBinding(object):
|
||||
"""ACME External Account Binding"""
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, account_public_key, kid, hmac_key, directory):
|
||||
"""Create External Account Binding Resource from contact details, kid and hmac."""
|
||||
|
||||
key_json = json.dumps(account_public_key.to_partial_json()).encode()
|
||||
decoded_hmac_key = jose.b64.b64decode(hmac_key)
|
||||
url = directory["newAccount"]
|
||||
|
||||
eab = jws.JWS.sign(key_json, jose.jwk.JWKOct(key=decoded_hmac_key),
|
||||
jose.jwa.HS256, None,
|
||||
url, kid)
|
||||
|
||||
return eab.to_partial_json()
|
||||
|
||||
|
||||
class Registration(ResourceBody):
|
||||
"""Registration Resource Body.
|
||||
|
||||
|
|
@ -275,12 +297,13 @@ class Registration(ResourceBody):
|
|||
status = jose.Field('status', omitempty=True)
|
||||
terms_of_service_agreed = jose.Field('termsOfServiceAgreed', omitempty=True)
|
||||
only_return_existing = jose.Field('onlyReturnExisting', omitempty=True)
|
||||
external_account_binding = jose.Field('externalAccountBinding', omitempty=True)
|
||||
|
||||
phone_prefix = 'tel:'
|
||||
email_prefix = 'mailto:'
|
||||
|
||||
@classmethod
|
||||
def from_data(cls, phone=None, email=None, **kwargs):
|
||||
def from_data(cls, phone=None, email=None, external_account_binding=None, **kwargs):
|
||||
"""Create registration resource from contact details."""
|
||||
details = list(kwargs.pop('contact', ()))
|
||||
if phone is not None:
|
||||
|
|
@ -288,6 +311,10 @@ class Registration(ResourceBody):
|
|||
if email is not None:
|
||||
details.extend([cls.email_prefix + mail for mail in email.split(',')])
|
||||
kwargs['contact'] = tuple(details)
|
||||
|
||||
if external_account_binding:
|
||||
kwargs['external_account_binding'] = external_account_binding
|
||||
|
||||
return cls(**kwargs)
|
||||
|
||||
def _filter_contact(self, prefix):
|
||||
|
|
|
|||
|
|
@ -174,6 +174,24 @@ class DirectoryTest(unittest.TestCase):
|
|||
self.assertTrue(result)
|
||||
|
||||
|
||||
class ExternalAccountBindingTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from acme.messages import Directory
|
||||
self.key = jose.jwk.JWKRSA(key=KEY.public_key())
|
||||
self.kid = "kid-for-testing"
|
||||
self.hmac_key = "hmac-key-for-testing"
|
||||
self.dir = Directory({
|
||||
'newAccount': 'http://url/acme/new-account',
|
||||
})
|
||||
|
||||
def test_from_data(self):
|
||||
from acme.messages import ExternalAccountBinding
|
||||
eab = ExternalAccountBinding.from_data(self.key, self.kid, self.hmac_key, self.dir)
|
||||
|
||||
self.assertEqual(len(eab), 3)
|
||||
self.assertEqual(sorted(eab.keys()), sorted(['protected', 'payload', 'signature']))
|
||||
|
||||
|
||||
class RegistrationTest(unittest.TestCase):
|
||||
"""Tests for acme.messages.Registration."""
|
||||
|
||||
|
|
@ -205,6 +223,22 @@ class RegistrationTest(unittest.TestCase):
|
|||
'mailto:admin@foo.com',
|
||||
))
|
||||
|
||||
def test_new_registration_from_data_with_eab(self):
|
||||
from acme.messages import NewRegistration, ExternalAccountBinding, Directory
|
||||
key = jose.jwk.JWKRSA(key=KEY.public_key())
|
||||
kid = "kid-for-testing"
|
||||
hmac_key = "hmac-key-for-testing"
|
||||
directory = Directory({
|
||||
'newAccount': 'http://url/acme/new-account',
|
||||
})
|
||||
eab = ExternalAccountBinding.from_data(key, kid, hmac_key, directory)
|
||||
reg = NewRegistration.from_data(email='admin@foo.com', external_account_binding=eab)
|
||||
self.assertEqual(reg.contact, (
|
||||
'mailto:admin@foo.com',
|
||||
))
|
||||
self.assertEqual(sorted(reg.external_account_binding.keys()),
|
||||
sorted(['protected', 'payload', 'signature']))
|
||||
|
||||
def test_phones(self):
|
||||
self.assertEqual(('1234',), self.reg.phones)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,21 +3,21 @@ from setuptools import find_packages
|
|||
from setuptools.command.test import test as TestCommand
|
||||
import sys
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
# load_pem_private/public_key (>=0.6)
|
||||
# rsa_recover_prime_factors (>=0.8)
|
||||
'cryptography>=0.8',
|
||||
'cryptography>=1.2.3',
|
||||
# formerly known as acme.jose:
|
||||
'josepy>=1.0.0',
|
||||
# Connection.set_tlsext_host_name (>=0.13)
|
||||
'mock',
|
||||
'PyOpenSSL>=0.13',
|
||||
'PyOpenSSL>=0.13.1',
|
||||
'pyrfc3339',
|
||||
'pytz',
|
||||
'requests[security]>=2.4.1', # security extras added in 2.4.1
|
||||
'requests[security]>=2.6.0', # security extras added in 2.4.1
|
||||
'requests-toolbelt>=0.3.0',
|
||||
'setuptools',
|
||||
'six>=1.9.0', # needed for python_2_unicode_compatible
|
||||
|
|
|
|||
18
appveyor.yml
18
appveyor.yml
|
|
@ -1,17 +1,9 @@
|
|||
image: Visual Studio 2015
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- FYI: Python 3.4 on Windows Server 2012 R2
|
||||
TOXENV: py34
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
|
||||
- FYI: Python 3.4 on Windows Server 2016
|
||||
TOXENV: py34
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
- FYI: Python 3.5 on Windows Server 2016
|
||||
TOXENV: py35
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
- FYI: Python 3.7 on Windows Server 2016 + code coverage
|
||||
TOXENV: py37-cover
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
- TOXENV: py35
|
||||
- TOXENV: py37-cover
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
|
@ -23,7 +15,6 @@ install:
|
|||
# Use Python 3.7 by default
|
||||
- "SET PATH=C:\\Python37;C:\\Python37\\Scripts;%PATH%"
|
||||
# Check env
|
||||
- "echo %APPVEYOR_BUILD_WORKER_IMAGE%"
|
||||
- "python --version"
|
||||
# Upgrade pip to avoid warnings
|
||||
- "python -m pip install --upgrade pip"
|
||||
|
|
@ -33,6 +24,7 @@ install:
|
|||
build: off
|
||||
|
||||
test_script:
|
||||
- set TOX_TESTENV_PASSENV=APPVEYOR
|
||||
# Test env is set by TOXENV env variable
|
||||
- tox
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
|
|||
|
||||
"""
|
||||
|
||||
description = "Apache Web Server plugin - Beta"
|
||||
description = "Apache Web Server plugin"
|
||||
|
||||
OS_DEFAULTS = dict(
|
||||
server_root="/etc/apache2",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@
|
|||
# A hackish script to see if the client is behaving as expected
|
||||
# with each of the "passing" conf files.
|
||||
|
||||
if [ -z "$SERVER" ]; then
|
||||
echo "Please set SERVER to the ACME server's directory URL."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export EA=/etc/apache2/
|
||||
TESTDIR="`dirname $0`"
|
||||
cd $TESTDIR/passing
|
||||
|
|
@ -56,13 +61,16 @@ if [ "$1" = --debian-modules ] ; then
|
|||
done
|
||||
fi
|
||||
|
||||
CERTBOT_CMD="sudo $(command -v certbot) --server $SERVER -vvvv"
|
||||
CERTBOT_CMD="$CERTBOT_CMD --debug --apache --register-unsafely-without-email"
|
||||
CERTBOT_CMD="$CERTBOT_CMD --agree-tos certonly -t --no-verify-ssl"
|
||||
|
||||
FAILS=0
|
||||
trap CleanupExit INT
|
||||
for f in *.conf ; do
|
||||
echo -n testing "$f"...
|
||||
Setup
|
||||
RESULT=`echo c | sudo $(command -v certbot) -vvvv --debug --staging --apache --register-unsafely-without-email --agree-tos certonly -t 2>&1`
|
||||
RESULT=`echo c | $CERTBOT_CMD 2>&1`
|
||||
if echo $RESULT | grep -Eq \("Which names would you like"\|"mod_macro is not yet"\) ; then
|
||||
echo passed
|
||||
else
|
||||
|
|
|
|||
|
|
@ -64,12 +64,12 @@ class AutoHSTSTest(util.ApacheTest):
|
|||
|
||||
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
||||
# Verify initial value
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
initial_val)
|
||||
# Increase
|
||||
self.config.update_autohsts(mock.MagicMock())
|
||||
# Verify increased value
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
inc_val)
|
||||
self.assertTrue(mock_prepare.called)
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ class AutoHSTSTest(util.ApacheTest):
|
|||
initial_val = maxage.format(constants.AUTOHSTS_STEPS[0])
|
||||
self.config.enable_autohsts(mock.MagicMock(), ["ocspvhost.com"])
|
||||
# Verify initial value
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
initial_val)
|
||||
|
||||
self.config.update_autohsts(mock.MagicMock())
|
||||
|
|
@ -112,19 +112,19 @@ class AutoHSTSTest(util.ApacheTest):
|
|||
for i in range(len(constants.AUTOHSTS_STEPS)-1):
|
||||
# Ensure that value is not made permanent prematurely
|
||||
self.config.deploy_autohsts(mock_lineage)
|
||||
self.assertNotEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertNotEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
max_val)
|
||||
self.config.update_autohsts(mock.MagicMock())
|
||||
# Value should match pre-permanent increment step
|
||||
cur_val = maxage.format(constants.AUTOHSTS_STEPS[i+1])
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
cur_val)
|
||||
# Ensure that the value is raised to max
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
maxage.format(constants.AUTOHSTS_STEPS[-1]))
|
||||
# Make permanent
|
||||
self.config.deploy_autohsts(mock_lineage)
|
||||
self.assertEquals(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
self.assertEqual(self.get_autohsts_value(self.vh_truth[7].path),
|
||||
max_val)
|
||||
|
||||
def test_autohsts_update_noop(self):
|
||||
|
|
@ -156,7 +156,7 @@ class AutoHSTSTest(util.ApacheTest):
|
|||
mock_id.return_value = "1234567"
|
||||
self.config.enable_autohsts(mock.MagicMock(),
|
||||
["ocspvhost.com", "ocspvhost.com"])
|
||||
self.assertEquals(mock_id.call_count, 1)
|
||||
self.assertEqual(mock_id.call_count, 1)
|
||||
|
||||
def test_autohsts_remove_orphaned(self):
|
||||
# pylint: disable=protected-access
|
||||
|
|
|
|||
|
|
@ -81,9 +81,9 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
|
|||
mock_osi.return_value = ("centos", "7")
|
||||
self.config.parser.update_runtime_variables()
|
||||
|
||||
self.assertEquals(mock_get.call_count, 3)
|
||||
self.assertEquals(len(self.config.parser.modules), 4)
|
||||
self.assertEquals(len(self.config.parser.variables), 2)
|
||||
self.assertEqual(mock_get.call_count, 3)
|
||||
self.assertEqual(len(self.config.parser.modules), 4)
|
||||
self.assertEqual(len(self.config.parser.variables), 2)
|
||||
self.assertTrue("TEST2" in self.config.parser.variables.keys())
|
||||
self.assertTrue("mod_another.c" in self.config.parser.modules)
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ class MultipleVhostsTestCentOS(util.ApacheTest):
|
|||
def test_alt_restart_works(self, mock_run_script):
|
||||
mock_run_script.side_effect = [None, errors.SubprocessError, None]
|
||||
self.config.restart()
|
||||
self.assertEquals(mock_run_script.call_count, 3)
|
||||
self.assertEqual(mock_run_script.call_count, 3)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.util.run_script")
|
||||
def test_alt_restart_errors(self, mock_run_script):
|
||||
|
|
|
|||
|
|
@ -1402,11 +1402,11 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
vhs = self.config._choose_vhosts_wildcard("*.certbot.demo",
|
||||
create_ssl=True)
|
||||
# Check that the dialog was called with one vh: certbot.demo
|
||||
self.assertEquals(mock_select_vhs.call_args[0][0][0], self.vh_truth[3])
|
||||
self.assertEquals(len(mock_select_vhs.call_args_list), 1)
|
||||
self.assertEqual(mock_select_vhs.call_args[0][0][0], self.vh_truth[3])
|
||||
self.assertEqual(len(mock_select_vhs.call_args_list), 1)
|
||||
|
||||
# And the actual returned values
|
||||
self.assertEquals(len(vhs), 1)
|
||||
self.assertEqual(len(vhs), 1)
|
||||
self.assertTrue(vhs[0].name == "certbot.demo")
|
||||
self.assertTrue(vhs[0].ssl)
|
||||
|
||||
|
|
@ -1421,7 +1421,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
vhs = self.config._choose_vhosts_wildcard("*.certbot.demo",
|
||||
create_ssl=False)
|
||||
self.assertFalse(mock_makessl.called)
|
||||
self.assertEquals(vhs[0], self.vh_truth[1])
|
||||
self.assertEqual(vhs[0], self.vh_truth[1])
|
||||
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator._vhosts_for_wildcard")
|
||||
@mock.patch("certbot_apache.configurator.ApacheConfigurator.make_vhost_ssl")
|
||||
|
|
@ -1434,15 +1434,15 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
mock_select_vhs.return_value = [self.vh_truth[7]]
|
||||
vhs = self.config._choose_vhosts_wildcard("whatever",
|
||||
create_ssl=True)
|
||||
self.assertEquals(mock_select_vhs.call_args[0][0][0], self.vh_truth[7])
|
||||
self.assertEquals(len(mock_select_vhs.call_args_list), 1)
|
||||
self.assertEqual(mock_select_vhs.call_args[0][0][0], self.vh_truth[7])
|
||||
self.assertEqual(len(mock_select_vhs.call_args_list), 1)
|
||||
# Ensure that make_vhost_ssl was not called, vhost.ssl == true
|
||||
self.assertFalse(mock_makessl.called)
|
||||
|
||||
# And the actual returned values
|
||||
self.assertEquals(len(vhs), 1)
|
||||
self.assertEqual(len(vhs), 1)
|
||||
self.assertTrue(vhs[0].ssl)
|
||||
self.assertEquals(vhs[0], self.vh_truth[7])
|
||||
self.assertEqual(vhs[0], self.vh_truth[7])
|
||||
|
||||
|
||||
def test_deploy_cert_wildcard(self):
|
||||
|
|
@ -1455,7 +1455,7 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
self.config.deploy_cert("*.wildcard.example.org", "/tmp/path",
|
||||
"/tmp/path", "/tmp/path", "/tmp/path")
|
||||
self.assertTrue(mock_dep.called)
|
||||
self.assertEquals(len(mock_dep.call_args_list), 1)
|
||||
self.assertEqual(len(mock_dep.call_args_list), 1)
|
||||
self.assertEqual(self.vh_truth[7], mock_dep.call_args_list[0][0][0])
|
||||
|
||||
@mock.patch("certbot_apache.display_ops.select_vhost_multiple")
|
||||
|
|
@ -1651,7 +1651,8 @@ class MultiVhostsTest(util.ApacheTest):
|
|||
self.assertTrue(self.config.parser.find_dir(
|
||||
"RewriteEngine", "on", ssl_vhost.path, False))
|
||||
|
||||
conf_text = open(ssl_vhost.filep).read()
|
||||
with open(ssl_vhost.filep) as the_file:
|
||||
conf_text = the_file.read()
|
||||
commented_rewrite_rule = ("# RewriteRule \"^/secrets/(.+)\" "
|
||||
"\"https://new.example.com/docs/$1\" [R,L]")
|
||||
uncommented_rewrite_rule = ("RewriteRule \"^/docs/(.+)\" "
|
||||
|
|
@ -1667,7 +1668,8 @@ class MultiVhostsTest(util.ApacheTest):
|
|||
|
||||
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[3])
|
||||
|
||||
conf_lines = open(ssl_vhost.filep).readlines()
|
||||
with open(ssl_vhost.filep) as the_file:
|
||||
conf_lines = the_file.readlines()
|
||||
conf_line_set = [l.strip() for l in conf_lines]
|
||||
not_commented_cond1 = ("RewriteCond "
|
||||
"%{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f")
|
||||
|
|
|
|||
|
|
@ -121,15 +121,15 @@ class MultipleVhostsTestGentoo(util.ApacheTest):
|
|||
mock_osi.return_value = ("gentoo", "123")
|
||||
self.config.parser.update_runtime_variables()
|
||||
|
||||
self.assertEquals(mock_get.call_count, 1)
|
||||
self.assertEquals(len(self.config.parser.modules), 4)
|
||||
self.assertEqual(mock_get.call_count, 1)
|
||||
self.assertEqual(len(self.config.parser.modules), 4)
|
||||
self.assertTrue("mod_another.c" in self.config.parser.modules)
|
||||
|
||||
@mock.patch("certbot_apache.configurator.util.run_script")
|
||||
def test_alt_restart_works(self, mock_run_script):
|
||||
mock_run_script.side_effect = [None, errors.SubprocessError, None]
|
||||
self.config.restart()
|
||||
self.assertEquals(mock_run_script.call_count, 3)
|
||||
self.assertEqual(mock_run_script.call_count, 3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class BasicParserTest(util.ParserTest):
|
|||
self.assertEqual(self.parser.aug.get(match), str(i + 1))
|
||||
|
||||
def test_empty_arg(self):
|
||||
self.assertEquals(None,
|
||||
self.assertEqual(None,
|
||||
self.parser.get_arg("/files/whatever/nonexistent"))
|
||||
|
||||
def test_add_dir_to_ifmodssl(self):
|
||||
|
|
@ -303,7 +303,7 @@ class BasicParserTest(util.ParserTest):
|
|||
from certbot_apache.parser import get_aug_path
|
||||
self.parser.add_comment(get_aug_path(self.parser.loc["name"]), "123456")
|
||||
comm = self.parser.find_comments("123456")
|
||||
self.assertEquals(len(comm), 1)
|
||||
self.assertEqual(len(comm), 1)
|
||||
self.assertTrue(self.parser.loc["name"] in comm[0])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
acme[dev]==0.25.0
|
||||
-e .[dev]
|
||||
certbot[dev]==0.26.0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
154
certbot-auto
154
certbot-auto
|
|
@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then
|
|||
fi
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt"
|
||||
LE_AUTO_VERSION="0.28.0"
|
||||
LE_AUTO_VERSION="0.30.0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
|
|
@ -593,8 +593,7 @@ BootstrapArchCommon() {
|
|||
# - ArchLinux (x86_64)
|
||||
#
|
||||
# "python-virtualenv" is Python3, but "python2-virtualenv" provides
|
||||
# only "virtualenv2" binary, not "virtualenv" necessary in
|
||||
# ./tools/_venv_common.sh
|
||||
# only "virtualenv2" binary, not "virtualenv".
|
||||
|
||||
deps="
|
||||
python2
|
||||
|
|
@ -912,6 +911,35 @@ OldVenvExists() {
|
|||
[ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ]
|
||||
}
|
||||
|
||||
# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2.
|
||||
# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated
|
||||
# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1
|
||||
# is outdated, and "UP_TO_DATE" if not.
|
||||
# This function relies only on installed python environment (2.x or 3.x) by certbot-auto.
|
||||
CompareVersions() {
|
||||
"$1" - "$2" "$3" << "UNLIKELY_EOF"
|
||||
import sys
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
try:
|
||||
current = StrictVersion(sys.argv[1])
|
||||
except ValueError:
|
||||
sys.stdout.write('UNOFFICIAL')
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
remote = StrictVersion(sys.argv[2])
|
||||
except ValueError:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
sys.exit()
|
||||
|
||||
if current < remote:
|
||||
sys.stdout.write('OUTDATED')
|
||||
else:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
UNLIKELY_EOF
|
||||
}
|
||||
|
||||
if [ "$1" = "--le-auto-phase2" ]; then
|
||||
# Phase 2: Create venv, install LE, and run.
|
||||
|
||||
|
|
@ -1017,43 +1045,39 @@ pycparser==2.14 \
|
|||
asn1crypto==0.22.0 \
|
||||
--hash=sha256:d232509fefcfcdb9a331f37e9c9dc20441019ad927c7d2176cf18ed5da0ba097 \
|
||||
--hash=sha256:cbbadd640d3165ab24b06ef25d1dca09a3441611ac15f6a6b452474fdf0aed1a
|
||||
cffi==1.10.0 \
|
||||
--hash=sha256:446699c10f3c390633d0722bc19edbc7ac4b94761918a4a4f7908a24e86ebbd0 \
|
||||
--hash=sha256:562326fc7f55a59ef3fef5e82908fe938cdc4bbda32d734c424c7cd9ed73e93a \
|
||||
--hash=sha256:7f732ad4a30db0b39400c3f7011249f7d0701007d511bf09604729aea222871f \
|
||||
--hash=sha256:94fb8410c6c4fc48e7ea759d3d1d9ca561171a88d00faddd4aa0306f698ad6a0 \
|
||||
--hash=sha256:587a5043df4b00a2130e09fed42da02a4ed3c688bd9bf07a3ac89d2271f4fb07 \
|
||||
--hash=sha256:ec08b88bef627ec1cea210e1608c85d3cf44893bcde74e41b7f7dbdfd2c1bad6 \
|
||||
--hash=sha256:a41406f6d62abcdf3eef9fd998d8dcff04fd2a7746644143045feeebd76352d1 \
|
||||
--hash=sha256:b560916546b2f209d74b82bdbc3223cee9a165b0242fa00a06dfc48a2054864a \
|
||||
--hash=sha256:e74896774e437f4715c57edeb5cf3d3a40d7727f541c2c12156617b5a15d1829 \
|
||||
--hash=sha256:9a31c18ba4881a116e448c52f3f5d3e14401cf7a9c43cc88f06f2a7f5428da0e \
|
||||
--hash=sha256:80796ea68e11624a0279d3b802f88a7fe7214122b97a15a6c97189934a2cc776 \
|
||||
--hash=sha256:f4019826a2dec066c909a1f483ef0dcf9325d6740cc0bd15308942b28b0930f7 \
|
||||
--hash=sha256:7248506981eeba23888b4140a69a53c4c0c0a386abcdca61ed8dd790a73e64b9 \
|
||||
--hash=sha256:a8955265d146e86fe2ce116394be4eaf0cb40314a79b19f11c4fa574cd639572 \
|
||||
--hash=sha256:c49187260043bd4c1d6a52186f9774f17d9b1da0a406798ebf4bfc12da166ade \
|
||||
--hash=sha256:c1d8b3d8dcb5c23ac1a8bf56422036f3f305a3c5a8bc8c354256579a1e2aa2c1 \
|
||||
--hash=sha256:9e389615bcecb8c782a87939d752340bb0a3a097e90bae54d7f0915bc12f45bd \
|
||||
--hash=sha256:d09ff358f75a874f69fa7d1c2b4acecf4282a950293fcfcf89aa606da8a9a500 \
|
||||
--hash=sha256:b69b4557aae7de18b7c174a917fe19873529d927ac592762d9771661875bbd40 \
|
||||
--hash=sha256:5de52b081a2775e76b971de9d997d85c4457fc0a09079e12d66849548ae60981 \
|
||||
--hash=sha256:e7d88fecb7b6250a1fd432e6dc64890342c372fce13dbfe4bb6f16348ad00c14 \
|
||||
--hash=sha256:1426e67e855ef7f5030c9184f4f1a9f4bfa020c31c962cd41fd129ec5aef4a6a \
|
||||
--hash=sha256:267dd2c66a5760c5f4d47e2ebcf8eeac7ef01e1ae6ae7a6d0d241a290068bc38 \
|
||||
--hash=sha256:e553eb489511cacf19eda6e52bc9e151316f0d721724997dda2c4d3079b778db \
|
||||
--hash=sha256:98b89b2c57f97ce2db7aeba60db173c84871d73b40e41a11ea95de1500ddc57e \
|
||||
--hash=sha256:e2b7e090188833bc58b2ae03fb864c22688654ebd2096bcf38bc860c4f38a3d8 \
|
||||
--hash=sha256:afa7d8b8d38ad40db8713ee053d41b36d87d6ae5ec5ad36f9210b548a18dc214 \
|
||||
--hash=sha256:4fc9c2ff7924b3a1fa326e1799e5dd58cac585d7fb25fe53ccaa1333b0453d65 \
|
||||
--hash=sha256:937db39a1ec5af3003b16357b2042bba67c88d43bc11aaa203fa8a5924524209 \
|
||||
--hash=sha256:ab22285797631df3b513b2cd3ecdc51cd8e3d36788e3991d93d0759d6883b027 \
|
||||
--hash=sha256:96e599b924ef009aa867f725b3249ee51d76489f484d3a45b4bd219c5ec6ed59 \
|
||||
--hash=sha256:bea842a0512be6a8007e585790bccd5d530520fc025ce63b03e139be373b0063 \
|
||||
--hash=sha256:e7175287f7fe7b1cc203bb958b17db40abd732690c1e18e700f10e0843a58598 \
|
||||
--hash=sha256:285ab352552f52f1398c912556d4d36d4ea9b8450e5c65d03809bf9886755533 \
|
||||
--hash=sha256:5576644b859197da7bbd8f8c7c2fb5dcc6cd505cadb42992d5f104c013f8a214 \
|
||||
--hash=sha256:b3b02911eb1f6ada203b0763ba924234629b51586f72a21faacc638269f4ced5
|
||||
cffi==1.11.5 \
|
||||
--hash=sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50 \
|
||||
--hash=sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596 \
|
||||
--hash=sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef \
|
||||
--hash=sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743 \
|
||||
--hash=sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f \
|
||||
--hash=sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31 \
|
||||
--hash=sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04 \
|
||||
--hash=sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6 \
|
||||
--hash=sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3 \
|
||||
--hash=sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6 \
|
||||
--hash=sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b \
|
||||
--hash=sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca \
|
||||
--hash=sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e \
|
||||
--hash=sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb \
|
||||
--hash=sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd \
|
||||
--hash=sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1 \
|
||||
--hash=sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917 \
|
||||
--hash=sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359 \
|
||||
--hash=sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f \
|
||||
--hash=sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95 \
|
||||
--hash=sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801 \
|
||||
--hash=sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257 \
|
||||
--hash=sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184 \
|
||||
--hash=sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc \
|
||||
--hash=sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085 \
|
||||
--hash=sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93 \
|
||||
--hash=sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2 \
|
||||
--hash=sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30 \
|
||||
--hash=sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5 \
|
||||
--hash=sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e \
|
||||
--hash=sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b \
|
||||
--hash=sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4
|
||||
ConfigArgParse==0.12.0 \
|
||||
--hash=sha256:28cd7d67669651f2a4518367838c49539457504584a139709b2b8f6c208ef339 \
|
||||
--no-binary ConfigArgParse
|
||||
|
|
@ -1146,9 +1170,9 @@ pytz==2015.7 \
|
|||
--hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \
|
||||
--hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \
|
||||
--hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3
|
||||
requests==2.12.1 \
|
||||
--hash=sha256:3f3f27a9d0f9092935efc78054ef324eb9f8166718270aefe036dfa1e4f68e1e \
|
||||
--hash=sha256:2109ecea94df90980be040490ff1d879971b024861539abb00054062388b612e
|
||||
requests==2.20.0 \
|
||||
--hash=sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c \
|
||||
--hash=sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279
|
||||
six==1.10.0 \
|
||||
--hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
|
||||
--hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
|
||||
|
|
@ -1185,6 +1209,15 @@ zope.interface==4.1.3 \
|
|||
requests-toolbelt==0.8.0 \
|
||||
--hash=sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237 \
|
||||
--hash=sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5
|
||||
chardet==3.0.2 \
|
||||
--hash=sha256:4f7832e7c583348a9eddd927ee8514b3bf717c061f57b21dbe7697211454d9bb \
|
||||
--hash=sha256:6ebf56457934fdce01fb5ada5582762a84eed94cad43ed877964aebbdd8174c0
|
||||
urllib3==1.24.1 \
|
||||
--hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \
|
||||
--hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22
|
||||
certifi==2017.4.17 \
|
||||
--hash=sha256:f4318671072f030a33c7ca6acaef720ddd50ff124d1388e50c1bda4cbd6d7010 \
|
||||
--hash=sha256:f7527ebf7461582ce95f7a9e03dd141ce810d40590834f4ec20cddd54234c10a
|
||||
|
||||
# Contains the requirements for the letsencrypt package.
|
||||
#
|
||||
|
|
@ -1197,31 +1230,29 @@ letsencrypt==0.7.0 \
|
|||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.28.0 \
|
||||
--hash=sha256:f2f7c816acd695cbcda713a779b0db2b08e9de407146b46e1c4ef5561e0f5417 \
|
||||
--hash=sha256:31e3e2ee2a25c009a621c59ac9182f85d937a897c7bd1d47d0e01f3c712a090a
|
||||
acme==0.28.0 \
|
||||
--hash=sha256:d3a564031155fece3f6edce8c5246d4cf34be0977b4c7c5ce84e86c68601c895 \
|
||||
--hash=sha256:bf7c2f1c24a26ab5b9fce3a6abca1d74a5914d46919649ae00ad5817db62bb85
|
||||
certbot-apache==0.28.0 \
|
||||
--hash=sha256:a57d7bac4f13ae5ecea4f4cbd479d381c02316c4832b25ab5a9d7c6826166370 \
|
||||
--hash=sha256:3f93f5de4a548e973c493a6cac5eeeb3dbbcae2988b61299ea0727d04a00f5bb
|
||||
certbot-nginx==0.28.0 \
|
||||
--hash=sha256:1822a65910f0801087fa20a3af3fc94f878f93e0f11809483bb5387df861e296 \
|
||||
--hash=sha256:426fb403b0a7b203629f4e350a862cbc3bc1f69936fdab8ec7eafe0d8a3b5ddb
|
||||
certbot==0.30.0 \
|
||||
--hash=sha256:b3468e128e74d2295598f6d3fbf9d0edfb67fe5abaca3b985a9e858395bd027f \
|
||||
--hash=sha256:d631fe6c75700ce9b2fdae194ff8b53c7518545d87dd451a1704f7572dcd49e8
|
||||
acme==0.30.0 \
|
||||
--hash=sha256:eed9389f802ebf4988c9e43c28ad3d5c2734237371d78e97450a1d61189a15aa \
|
||||
--hash=sha256:984b6d00bec73dcfa616636a760e80ca14bd246fb908710a656547f542f09445
|
||||
certbot-apache==0.30.0 \
|
||||
--hash=sha256:d38c70fc6930db298ea992a3145362eebdce460d3d2651f86a8f2f43d838c6d0 \
|
||||
--hash=sha256:1d4bc207d53a3e5d37e5d9ebd05f26089aa21d1fbf384113ed9d1829b4d1e9bf
|
||||
certbot-nginx==0.30.0 \
|
||||
--hash=sha256:6163c7d0080f59b4ebe510afcc6af2d2eebf15469275c3835866690db4d465d6 \
|
||||
--hash=sha256:e39a3f3d77cd4c653949cf066fb2211039fd2032665697c27b6e8501c7c2dd92
|
||||
|
||||
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
|
||||
|
|
@ -1348,10 +1379,8 @@ def hashed_download(url, temp, digest):
|
|||
|
||||
def get_index_base():
|
||||
"""Return the URL to the dir containing the "packages" folder.
|
||||
|
||||
Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the
|
||||
end if it's there; that is likely to give us the right dir.
|
||||
|
||||
"""
|
||||
env_var = environ.get('PIP_INDEX_URL', '').rstrip('/')
|
||||
if env_var:
|
||||
|
|
@ -1641,7 +1670,12 @@ UNLIKELY_EOF
|
|||
error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates."
|
||||
elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
error "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
fi
|
||||
|
||||
LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"`
|
||||
if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then
|
||||
say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION"
|
||||
elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then
|
||||
say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot',
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class _CloudflareClient(object):
|
|||
self.cf.zones.dns_records.delete(zone_id, record_id)
|
||||
logger.debug('Successfully deleted TXT record.')
|
||||
except CloudFlare.exceptions.CloudFlareAPIError as e:
|
||||
logger.warn('Encountered CloudFlareAPIError deleting TXT record: %s', e)
|
||||
logger.warning('Encountered CloudFlareAPIError deleting TXT record: %s', e)
|
||||
else:
|
||||
logger.debug('TXT record not found; no cleanup needed.')
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ class _DigitalOceanClient(object):
|
|||
logger.debug('Removing TXT record with id: %s', record.id)
|
||||
record.destroy()
|
||||
except digitalocean.Error as e:
|
||||
logger.warn('Error deleting TXT record %s using the DigitalOcean API: %s',
|
||||
logger.warning('Error deleting TXT record %s using the DigitalOcean API: %s',
|
||||
record.id, e)
|
||||
|
||||
def _find_domain(self, domain_name):
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ Examples
|
|||
|
||||
certbot certonly \\
|
||||
--dns-google \\
|
||||
--dns-google-credentials ~/.secrets/certbot/google.ini \\
|
||||
--dns-google-credentials ~/.secrets/certbot/google.json \\
|
||||
--dns-google-propagation-seconds 120 \\
|
||||
-d example.com
|
||||
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ class _GoogleClient(object):
|
|||
try:
|
||||
zone_id = self._find_managed_zone_id(domain)
|
||||
except errors.PluginError as e:
|
||||
logger.warn('Error finding zone. Skipping cleanup.')
|
||||
logger.warning('Error finding zone. Skipping cleanup.')
|
||||
return
|
||||
|
||||
record_contents = self.get_existing_txt_rrset(zone_id, record_name)
|
||||
|
|
@ -219,7 +219,7 @@ class _GoogleClient(object):
|
|||
request = changes.create(project=self.project_id, managedZone=zone_id, body=data)
|
||||
request.execute()
|
||||
except googleapiclient_errors.Error as e:
|
||||
logger.warn('Encountered error deleting TXT record: %s', e)
|
||||
logger.warning('Encountered error deleting TXT record: %s', e)
|
||||
|
||||
def get_existing_txt_rrset(self, zone_id, record_name):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -276,9 +276,9 @@ class GoogleClientTest(unittest.TestCase):
|
|||
[{'managedZones': [{'id': self.zone}]}])
|
||||
# Record name mocked in setUp
|
||||
found = client.get_existing_txt_rrset(self.zone, "_acme-challenge.example.org")
|
||||
self.assertEquals(found, ["\"example-txt-contents\""])
|
||||
self.assertEqual(found, ["\"example-txt-contents\""])
|
||||
not_found = client.get_existing_txt_rrset(self.zone, "nonexistent.tld")
|
||||
self.assertEquals(not_found, None)
|
||||
self.assertEqual(not_found, None)
|
||||
|
||||
@mock.patch('oauth2client.service_account.ServiceAccountCredentials.from_json_keyfile_name')
|
||||
@mock.patch('certbot_dns_google.dns_google.open',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import sys
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ def _parse_ssl_options(ssl_options):
|
|||
with open(ssl_options) as _file:
|
||||
return nginxparser.load(_file)
|
||||
except IOError:
|
||||
logger.warn("Missing NGINX TLS options file: %s", ssl_options)
|
||||
logger.warning("Missing NGINX TLS options file: %s", ssl_options)
|
||||
except pyparsing.ParseBaseException as err:
|
||||
logger.debug("Could not parse file: %s due to %s", ssl_options, err)
|
||||
return []
|
||||
|
|
|
|||
|
|
@ -194,9 +194,9 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
|
||||
def test_ipv6only(self):
|
||||
# ipv6_info: (ipv6_active, ipv6only_present)
|
||||
self.assertEquals((True, False), self.config.ipv6_info("80"))
|
||||
self.assertEqual((True, False), self.config.ipv6_info("80"))
|
||||
# Port 443 has ipv6only=on because of ipv6ssl.com vhost
|
||||
self.assertEquals((True, True), self.config.ipv6_info("443"))
|
||||
self.assertEqual((True, True), self.config.ipv6_info("443"))
|
||||
|
||||
def test_ipv6only_detection(self):
|
||||
self.config.version = (1, 3, 1)
|
||||
|
|
@ -807,7 +807,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
|
||||
|
||||
# And the actual returned values
|
||||
self.assertEquals(len(vhs), 1)
|
||||
self.assertEqual(len(vhs), 1)
|
||||
self.assertEqual(vhs[0], vhost)
|
||||
|
||||
def test_choose_vhosts_wildcard_redirect(self):
|
||||
|
|
@ -823,7 +823,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
self.assertTrue(vhost in mock_select_vhs.call_args[0][0])
|
||||
|
||||
# And the actual returned values
|
||||
self.assertEquals(len(vhs), 1)
|
||||
self.assertEqual(len(vhs), 1)
|
||||
self.assertEqual(vhs[0], vhost)
|
||||
|
||||
def test_deploy_cert_wildcard(self):
|
||||
|
|
@ -838,7 +838,7 @@ class NginxConfiguratorTest(util.NginxTest):
|
|||
self.config.deploy_cert("*.com", "/tmp/path",
|
||||
"/tmp/path", "/tmp/path", "/tmp/path")
|
||||
self.assertTrue(mock_dep.called)
|
||||
self.assertEquals(len(mock_dep.call_args_list), 1)
|
||||
self.assertEqual(len(mock_dep.call_args_list), 1)
|
||||
self.assertEqual(vhost, mock_dep.call_args_list[0][0][0])
|
||||
|
||||
@mock.patch("certbot_nginx.display_ops.select_vhost_multiple")
|
||||
|
|
|
|||
|
|
@ -103,37 +103,37 @@ class SentenceTest(unittest.TestCase):
|
|||
def test_parse_sentence_words_hides_spaces(self):
|
||||
og_sentence = ['\r\n', 'hello', ' ', ' ', '\t\n ', 'lol', ' ', 'spaces']
|
||||
self.sentence.parse(og_sentence)
|
||||
self.assertEquals(self.sentence.words, ['hello', 'lol', 'spaces'])
|
||||
self.assertEquals(self.sentence.dump(), ['hello', 'lol', 'spaces'])
|
||||
self.assertEquals(self.sentence.dump(True), og_sentence)
|
||||
self.assertEqual(self.sentence.words, ['hello', 'lol', 'spaces'])
|
||||
self.assertEqual(self.sentence.dump(), ['hello', 'lol', 'spaces'])
|
||||
self.assertEqual(self.sentence.dump(True), og_sentence)
|
||||
|
||||
def test_parse_sentence_with_add_spaces(self):
|
||||
self.sentence.parse(['hi', 'there'], add_spaces=True)
|
||||
self.assertEquals(self.sentence.dump(True), ['hi', ' ', 'there'])
|
||||
self.assertEqual(self.sentence.dump(True), ['hi', ' ', 'there'])
|
||||
self.sentence.parse(['one', ' ', 'space', 'none'], add_spaces=True)
|
||||
self.assertEquals(self.sentence.dump(True), ['one', ' ', 'space', ' ', 'none'])
|
||||
self.assertEqual(self.sentence.dump(True), ['one', ' ', 'space', ' ', 'none'])
|
||||
|
||||
def test_iterate(self):
|
||||
expected = [['1', '2', '3']]
|
||||
self.sentence.parse(['1', ' ', '2', ' ', '3'])
|
||||
for i, sentence in enumerate(self.sentence.iterate()):
|
||||
self.assertEquals(sentence.dump(), expected[i])
|
||||
self.assertEqual(sentence.dump(), expected[i])
|
||||
|
||||
def test_set_tabs(self):
|
||||
self.sentence.parse(['tabs', 'pls'], add_spaces=True)
|
||||
self.sentence.set_tabs()
|
||||
self.assertEquals(self.sentence.dump(True)[0], '\n ')
|
||||
self.assertEqual(self.sentence.dump(True)[0], '\n ')
|
||||
self.sentence.parse(['tabs', 'pls'], add_spaces=True)
|
||||
|
||||
def test_get_tabs(self):
|
||||
self.sentence.parse(['no', 'tabs'])
|
||||
self.assertEquals(self.sentence.get_tabs(), '')
|
||||
self.assertEqual(self.sentence.get_tabs(), '')
|
||||
self.sentence.parse(['\n \n ', 'tabs'])
|
||||
self.assertEquals(self.sentence.get_tabs(), ' ')
|
||||
self.assertEqual(self.sentence.get_tabs(), ' ')
|
||||
self.sentence.parse(['\n\t ', 'tabs'])
|
||||
self.assertEquals(self.sentence.get_tabs(), '\t ')
|
||||
self.assertEqual(self.sentence.get_tabs(), '\t ')
|
||||
self.sentence.parse(['\n\t \n', 'tabs'])
|
||||
self.assertEquals(self.sentence.get_tabs(), '')
|
||||
self.assertEqual(self.sentence.get_tabs(), '')
|
||||
|
||||
class BlockTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -145,11 +145,11 @@ class BlockTest(unittest.TestCase):
|
|||
|
||||
def test_iterate(self):
|
||||
# Iterates itself normally
|
||||
self.assertEquals(self.bloc, next(self.bloc.iterate()))
|
||||
self.assertEqual(self.bloc, next(self.bloc.iterate()))
|
||||
# Iterates contents while expanded
|
||||
expected = [self.bloc.dump()] + self.contents
|
||||
for i, elem in enumerate(self.bloc.iterate(expanded=True)):
|
||||
self.assertEquals(expected[i], elem.dump())
|
||||
self.assertEqual(expected[i], elem.dump())
|
||||
|
||||
def test_iterate_match(self):
|
||||
# can match on contents while expanded
|
||||
|
|
@ -157,17 +157,17 @@ class BlockTest(unittest.TestCase):
|
|||
expected = [['thing', '1'], ['thing', '2']]
|
||||
for i, elem in enumerate(self.bloc.iterate(expanded=True,
|
||||
match=lambda x: isinstance(x, Sentence) and 'thing' in x.words)):
|
||||
self.assertEquals(expected[i], elem.dump())
|
||||
self.assertEqual(expected[i], elem.dump())
|
||||
# can match on self
|
||||
self.assertEquals(self.bloc, next(self.bloc.iterate(
|
||||
self.assertEqual(self.bloc, next(self.bloc.iterate(
|
||||
expanded=True,
|
||||
match=lambda x: isinstance(x, Block) and 'server' in x.names)))
|
||||
|
||||
def test_parse_with_added_spaces(self):
|
||||
import copy
|
||||
self.bloc.parse([copy.copy(self.name), self.contents], add_spaces=True)
|
||||
self.assertEquals(self.bloc.dump(), [self.name, self.contents])
|
||||
self.assertEquals(self.bloc.dump(True), [
|
||||
self.assertEqual(self.bloc.dump(), [self.name, self.contents])
|
||||
self.assertEqual(self.bloc.dump(True), [
|
||||
['server', ' ', 'name', ' '],
|
||||
[['thing', ' ', '1'],
|
||||
['thing', ' ', '2'],
|
||||
|
|
@ -181,14 +181,14 @@ class BlockTest(unittest.TestCase):
|
|||
|
||||
def test_set_tabs(self):
|
||||
self.bloc.set_tabs()
|
||||
self.assertEquals(self.bloc.names.dump(True)[0], '\n ')
|
||||
self.assertEqual(self.bloc.names.dump(True)[0], '\n ')
|
||||
for elem in self.bloc.contents.dump(True)[:-1]:
|
||||
self.assertEquals(elem[0], '\n ')
|
||||
self.assertEquals(self.bloc.contents.dump(True)[-1][0], '\n')
|
||||
self.assertEqual(elem[0], '\n ')
|
||||
self.assertEqual(self.bloc.contents.dump(True)[-1][0], '\n')
|
||||
|
||||
def test_get_tabs(self):
|
||||
self.bloc.parse([[' \n \t', 'lol'], []])
|
||||
self.assertEquals(self.bloc.get_tabs(), ' \t')
|
||||
self.assertEqual(self.bloc.get_tabs(), ' \t')
|
||||
|
||||
class StatementsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -210,7 +210,7 @@ class StatementsTest(unittest.TestCase):
|
|||
self.statements.parse(self.raw)
|
||||
self.statements.set_tabs()
|
||||
for statement in self.statements.iterate():
|
||||
self.assertEquals(statement.dump(True)[0], '\n ')
|
||||
self.assertEqual(statement.dump(True)[0], '\n ')
|
||||
|
||||
def test_set_tabs_with_parent(self):
|
||||
# Trailing whitespace should inherit from parent tabbing.
|
||||
|
|
@ -219,19 +219,19 @@ class StatementsTest(unittest.TestCase):
|
|||
self.statements.parent.get_tabs.return_value = '\t\t'
|
||||
self.statements.set_tabs()
|
||||
for statement in self.statements.iterate():
|
||||
self.assertEquals(statement.dump(True)[0], '\n ')
|
||||
self.assertEquals(self.statements.dump(True)[-1], '\n\t\t')
|
||||
self.assertEqual(statement.dump(True)[0], '\n ')
|
||||
self.assertEqual(self.statements.dump(True)[-1], '\n\t\t')
|
||||
|
||||
def test_get_tabs(self):
|
||||
self.raw[0].insert(0, '\n \n \t')
|
||||
self.statements.parse(self.raw)
|
||||
self.assertEquals(self.statements.get_tabs(), ' \t')
|
||||
self.assertEqual(self.statements.get_tabs(), ' \t')
|
||||
self.statements.parse([])
|
||||
self.assertEquals(self.statements.get_tabs(), '')
|
||||
self.assertEqual(self.statements.get_tabs(), '')
|
||||
|
||||
def test_parse_with_added_spaces(self):
|
||||
self.statements.parse(self.raw, add_spaces=True)
|
||||
self.assertEquals(self.statements.dump(True)[0], ['sentence', ' ', 'one'])
|
||||
self.assertEqual(self.statements.dump(True)[0], ['sentence', ' ', 'one'])
|
||||
|
||||
def test_parse_bad_list_raises_error(self):
|
||||
from certbot import errors
|
||||
|
|
@ -241,13 +241,13 @@ class StatementsTest(unittest.TestCase):
|
|||
self.statements.parse(self.raw + ['\n\n '])
|
||||
self.assertTrue(isinstance(self.statements.dump()[-1], list))
|
||||
self.assertTrue(self.statements.dump(True)[-1].isspace())
|
||||
self.assertEquals(self.statements.dump(True)[-1], '\n\n ')
|
||||
self.assertEqual(self.statements.dump(True)[-1], '\n\n ')
|
||||
|
||||
def test_iterate(self):
|
||||
self.statements.parse(self.raw)
|
||||
expected = [['sentence', 'one'], ['sentence', 'two']]
|
||||
for i, elem in enumerate(self.statements.iterate(match=lambda x: 'sentence' in x)):
|
||||
self.assertEquals(expected[i], elem.dump())
|
||||
self.assertEqual(expected[i], elem.dump())
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main() # pragma: no cover
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.29.0.dev0'
|
||||
version = '0.31.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class PostConfTest(unittest.TestCase):
|
|||
self.config.set('extra_param', 'another_value')
|
||||
self.config.flush()
|
||||
arguments = mock_out.call_args_list[-1][0][0]
|
||||
self.assertEquals('-e', arguments[0])
|
||||
self.assertEqual('-e', arguments[0])
|
||||
self.assertTrue('default_parameter=new_value' in arguments)
|
||||
self.assertTrue('extra_param=another_value' in arguments)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Certbot client."""
|
||||
|
||||
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
|
||||
__version__ = '0.29.0.dev0'
|
||||
__version__ = '0.31.0.dev0'
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ manage certificates:
|
|||
|
||||
manage your account with Let's Encrypt:
|
||||
register Create a Let's Encrypt ACME account
|
||||
update_account Update a Let's Encrypt ACME account
|
||||
--agree-tos Agree to the ACME server's Subscriber Agreement
|
||||
-m EMAIL Email address for important account notifications
|
||||
"""
|
||||
|
|
@ -172,10 +173,11 @@ def possible_deprecation_warning(config):
|
|||
# need warnings
|
||||
return
|
||||
if "CERTBOT_AUTO" not in os.environ:
|
||||
logger.warning("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.")
|
||||
logger.warning("You are running with an old copy of letsencrypt-auto"
|
||||
" that does not receive updates, and is less reliable than more"
|
||||
" recent versions. The letsencrypt client has also been renamed"
|
||||
" to Certbot. We recommend upgrading to the latest certbot-auto"
|
||||
" script, or using native OS packages.")
|
||||
logger.debug("Deprecation warning circumstances: %s / %s", sys.argv[0], os.environ)
|
||||
|
||||
|
||||
|
|
@ -286,7 +288,9 @@ def read_file(filename, mode="rb"):
|
|||
"""
|
||||
try:
|
||||
filename = os.path.abspath(filename)
|
||||
return filename, open(filename, mode).read()
|
||||
with open(filename, mode) as the_file:
|
||||
contents = the_file.read()
|
||||
return filename, contents
|
||||
except IOError as exc:
|
||||
raise argparse.ArgumentTypeError(exc.strerror)
|
||||
|
||||
|
|
@ -394,9 +398,14 @@ VERB_HELP = [
|
|||
}),
|
||||
("register", {
|
||||
"short": "Register for account with Let's Encrypt / other ACME server",
|
||||
"opts": "Options for account registration & modification",
|
||||
"opts": "Options for account registration",
|
||||
"usage": "\n\n certbot register --email user@example.com [options]\n\n"
|
||||
}),
|
||||
("update_account", {
|
||||
"short": "Update existing account with Let's Encrypt / other ACME server",
|
||||
"opts": "Options for account modification",
|
||||
"usage": "\n\n certbot update_account --email updated_email@example.com [options]\n\n"
|
||||
}),
|
||||
("unregister", {
|
||||
"short": "Irrevocably deactivate your account",
|
||||
"opts": "Options for account deactivation.",
|
||||
|
|
@ -462,6 +471,7 @@ class HelpfulArgumentParser(object):
|
|||
"install": main.install,
|
||||
"plugins": main.plugins_cmd,
|
||||
"register": main.register,
|
||||
"update_account": main.update_account,
|
||||
"unregister": main.unregister,
|
||||
"renew": main.renew,
|
||||
"revoke": main.revoke,
|
||||
|
|
@ -856,7 +866,9 @@ class HelpfulArgumentParser(object):
|
|||
if chosen_topic == "everything":
|
||||
chosen_topic = "run"
|
||||
if chosen_topic == "all":
|
||||
return dict([(t, True) for t in self.help_topics])
|
||||
# Addition of condition closes #6209 (removal of duplicate route53 option).
|
||||
return dict([(t, True) if t != 'certbot-route53:auth' else (t, False)
|
||||
for t in self.help_topics])
|
||||
elif not chosen_topic:
|
||||
return dict([(t, False) for t in self.help_topics])
|
||||
else:
|
||||
|
|
@ -941,6 +953,18 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
|||
"specified or you already have a certificate with the same "
|
||||
"name. In the case of a name collision it will append a number "
|
||||
"like 0001 to the file path name. (default: Ask)")
|
||||
helpful.add(
|
||||
[None, "run", "certonly", "register"],
|
||||
"--eab-kid", dest="eab_kid",
|
||||
metavar="EAB_KID",
|
||||
help="Key Identifier for External Account Binding"
|
||||
)
|
||||
helpful.add(
|
||||
[None, "run", "certonly", "register"],
|
||||
"--eab-hmac-key", dest="eab_hmac_key",
|
||||
metavar="EAB_HMAC_KEY",
|
||||
help="HMAC key for External Account Binding"
|
||||
)
|
||||
helpful.add(
|
||||
[None, "run", "certonly", "manage", "delete", "certificates",
|
||||
"renew", "enhance"], "--cert-name", dest="certname",
|
||||
|
|
@ -976,21 +1000,21 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
|||
"certificates. Updates to the Subscriber Agreement will still "
|
||||
"affect you, and will be effective 14 days after posting an "
|
||||
"update to the web site.")
|
||||
# TODO: When `certbot register --update-registration` is fully deprecated,
|
||||
# delete following helpful.add
|
||||
helpful.add(
|
||||
"register", "--update-registration", action="store_true",
|
||||
default=flag_default("update_registration"),
|
||||
help="With the register verb, indicates that details associated "
|
||||
"with an existing registration, such as the e-mail address, "
|
||||
"should be updated, rather than registering a new account.")
|
||||
default=flag_default("update_registration"), dest="update_registration",
|
||||
help=argparse.SUPPRESS)
|
||||
helpful.add(
|
||||
["register", "unregister", "automation"], "-m", "--email",
|
||||
["register", "update_account", "unregister", "automation"], "-m", "--email",
|
||||
default=flag_default("email"),
|
||||
help=config_help("email"))
|
||||
helpful.add(["register", "automation"], "--eff-email", action="store_true",
|
||||
helpful.add(["register", "update_account", "automation"], "--eff-email", action="store_true",
|
||||
default=flag_default("eff_email"), dest="eff_email",
|
||||
help="Share your e-mail address with EFF")
|
||||
helpful.add(["register", "automation"], "--no-eff-email", action="store_false",
|
||||
default=flag_default("eff_email"), dest="eff_email",
|
||||
helpful.add(["register", "update_account", "automation"], "--no-eff-email",
|
||||
action="store_false", default=flag_default("eff_email"), dest="eff_email",
|
||||
help="Don't share your e-mail address with EFF")
|
||||
helpful.add(
|
||||
["automation", "certonly", "run"],
|
||||
|
|
@ -1191,6 +1215,10 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis
|
|||
" one will be run.")
|
||||
helpful.add("renew", "--renew-hook",
|
||||
action=_RenewHookAction, help=argparse.SUPPRESS)
|
||||
helpful.add(
|
||||
"renew", "--no-random-sleep-on-renew", action="store_false",
|
||||
default=flag_default("random_sleep_on_renew"), dest="random_sleep_on_renew",
|
||||
help=argparse.SUPPRESS)
|
||||
helpful.add(
|
||||
"renew", "--deploy-hook", action=_DeployHookAction,
|
||||
help='Command to be run in a shell once for each successfully'
|
||||
|
|
|
|||
|
|
@ -203,9 +203,27 @@ def perform_registration(acme, config, tos_cb):
|
|||
:returns: Registration Resource.
|
||||
:rtype: `acme.messages.RegistrationResource`
|
||||
"""
|
||||
|
||||
eab_credentials_supplied = config.eab_kid and config.eab_hmac_key
|
||||
if eab_credentials_supplied:
|
||||
account_public_key = acme.client.net.key.public_key()
|
||||
eab = messages.ExternalAccountBinding.from_data(account_public_key=account_public_key,
|
||||
kid=config.eab_kid,
|
||||
hmac_key=config.eab_hmac_key,
|
||||
directory=acme.client.directory)
|
||||
else:
|
||||
eab = None
|
||||
|
||||
if acme.external_account_required():
|
||||
if not eab_credentials_supplied:
|
||||
msg = ("Server requires external account binding."
|
||||
" Please use --eab-kid and --eab-hmac-key.")
|
||||
raise errors.Error(msg)
|
||||
|
||||
try:
|
||||
return acme.new_account_and_tos(messages.NewRegistration.from_data(email=config.email),
|
||||
tos_cb)
|
||||
newreg = messages.NewRegistration.from_data(email=config.email,
|
||||
external_account_binding=eab)
|
||||
return acme.new_account_and_tos(newreg, tos_cb)
|
||||
except messages.Error as e:
|
||||
if e.code == "invalidEmail" or e.code == "invalidContact":
|
||||
if config.noninteractive_mode:
|
||||
|
|
|
|||
|
|
@ -172,3 +172,30 @@ def compare_file_modes(mode1, mode2):
|
|||
# Windows specific: most of mode bits are ignored on Windows. Only check user R/W rights.
|
||||
return (stat.S_IMODE(mode1) & stat.S_IREAD == stat.S_IMODE(mode2) & stat.S_IREAD
|
||||
and stat.S_IMODE(mode1) & stat.S_IWRITE == stat.S_IMODE(mode2) & stat.S_IWRITE)
|
||||
|
||||
WINDOWS_DEFAULT_FOLDERS = {
|
||||
'config': 'C:\\Certbot',
|
||||
'work': 'C:\\Certbot\\lib',
|
||||
'logs': 'C:\\Certbot\\log',
|
||||
}
|
||||
LINUX_DEFAULT_FOLDERS = {
|
||||
'config': '/etc/letsencrypt',
|
||||
'work': '/var/lib/letsencrypt',
|
||||
'logs': '/var/log/letsencrypt',
|
||||
}
|
||||
|
||||
def get_default_folder(folder_type):
|
||||
"""
|
||||
Return the relevant default folder for the current OS
|
||||
|
||||
:param str folder_type: The type of folder to retrieve (config, work or logs)
|
||||
|
||||
:returns: The relevant default folder.
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
if 'fcntl' in sys.modules:
|
||||
# Linux specific
|
||||
return LINUX_DEFAULT_FOLDERS[folder_type]
|
||||
# Windows specific
|
||||
return WINDOWS_DEFAULT_FOLDERS[folder_type]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import os
|
|||
import pkg_resources
|
||||
|
||||
from acme import challenges
|
||||
|
||||
from certbot import compat
|
||||
|
||||
SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins"
|
||||
"""Setuptools entry point group name for plugins."""
|
||||
|
|
@ -14,7 +14,7 @@ OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins"
|
|||
|
||||
CLI_DEFAULTS = dict(
|
||||
config_files=[
|
||||
"/etc/letsencrypt/cli.ini",
|
||||
os.path.join(compat.get_default_folder('config'), 'cli.ini'),
|
||||
# http://freedesktop.org/wiki/Software/xdg-user-dirs/
|
||||
os.path.join(os.environ.get("XDG_CONFIG_HOME", "~/.config"),
|
||||
"letsencrypt", "cli.ini"),
|
||||
|
|
@ -68,6 +68,9 @@ CLI_DEFAULTS = dict(
|
|||
directory_hooks=True,
|
||||
reuse_key=False,
|
||||
disable_renew_updates=False,
|
||||
random_sleep_on_renew=True,
|
||||
eab_hmac_key=None,
|
||||
eab_kid=None,
|
||||
|
||||
# Subparsers
|
||||
num=None,
|
||||
|
|
@ -85,9 +88,9 @@ CLI_DEFAULTS = dict(
|
|||
auth_cert_path="./cert.pem",
|
||||
auth_chain_path="./chain.pem",
|
||||
key_path=None,
|
||||
config_dir="/etc/letsencrypt",
|
||||
work_dir="/var/lib/letsencrypt",
|
||||
logs_dir="/var/log/letsencrypt",
|
||||
config_dir=compat.get_default_folder('config'),
|
||||
work_dir=compat.get_default_folder('work'),
|
||||
logs_dir=compat.get_default_folder('logs'),
|
||||
server="https://acme-v02.api.letsencrypt.org/directory",
|
||||
|
||||
# Plugins parsers
|
||||
|
|
|
|||
|
|
@ -458,7 +458,7 @@ def sha256sum(filename):
|
|||
:rtype: str
|
||||
"""
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filename, 'rU') as file_d:
|
||||
with open(filename, 'r') as file_d:
|
||||
sha256.update(file_d.read().encode('UTF-8'))
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ import os
|
|||
|
||||
import zope.component
|
||||
|
||||
from certbot import compat
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import util
|
||||
|
||||
from certbot.display import util as display_util
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -33,7 +35,8 @@ def get_email(invalid=False, optional=True):
|
|||
unsafe_suggestion = ("\n\nIf you really want to skip this, you can run "
|
||||
"the client with --register-unsafely-without-email "
|
||||
"but make sure you then backup your account key from "
|
||||
"/etc/letsencrypt/accounts\n\n")
|
||||
"{0}\n\n".format(os.path.join(
|
||||
compat.get_default_folder('config'), 'accounts')))
|
||||
if optional:
|
||||
if invalid:
|
||||
msg += unsafe_suggestion
|
||||
|
|
|
|||
|
|
@ -652,7 +652,45 @@ def unregister(config, unused_plugins):
|
|||
|
||||
|
||||
def register(config, unused_plugins):
|
||||
"""Create or modify accounts on the server.
|
||||
"""Create accounts on the server.
|
||||
|
||||
:param config: Configuration object
|
||||
:type config: interfaces.IConfig
|
||||
|
||||
:param unused_plugins: List of plugins (deprecated)
|
||||
:type unused_plugins: `list` of `str`
|
||||
|
||||
:returns: `None` or a string indicating and error
|
||||
:rtype: None or str
|
||||
|
||||
"""
|
||||
# TODO: When `certbot register --update-registration` is fully deprecated,
|
||||
# delete the true case of if block
|
||||
if config.update_registration:
|
||||
msg = ("Usage 'certbot register --update-registration' is deprecated.\n"
|
||||
"Please use 'cerbot update_account [options]' instead.\n")
|
||||
logger.warning(msg)
|
||||
return update_account(config, unused_plugins)
|
||||
|
||||
# Portion of _determine_account logic to see whether accounts already
|
||||
# exist or not.
|
||||
account_storage = account.AccountFileStorage(config)
|
||||
accounts = account_storage.find_all()
|
||||
|
||||
if len(accounts) > 0:
|
||||
# TODO: add a flag to register a duplicate account (this will
|
||||
# also require extending _determine_account's behavior
|
||||
# or else extracting the registration code from there)
|
||||
return ("There is an existing account; registration of a "
|
||||
"duplicate account with this command is currently "
|
||||
"unsupported.")
|
||||
# _determine_account will register an account
|
||||
_determine_account(config)
|
||||
return
|
||||
|
||||
|
||||
def update_account(config, unused_plugins):
|
||||
"""Modify accounts on the server.
|
||||
|
||||
:param config: Configuration object
|
||||
:type config: interfaces.IConfig
|
||||
|
|
@ -671,20 +709,6 @@ def register(config, unused_plugins):
|
|||
reporter_util = zope.component.getUtility(interfaces.IReporter)
|
||||
add_msg = lambda m: reporter_util.add_message(m, reporter_util.MEDIUM_PRIORITY)
|
||||
|
||||
# registering a new account
|
||||
if not config.update_registration:
|
||||
if len(accounts) > 0:
|
||||
# TODO: add a flag to register a duplicate account (this will
|
||||
# also require extending _determine_account's behavior
|
||||
# or else extracting the registration code from there)
|
||||
return ("There is an existing account; registration of a "
|
||||
"duplicate account with this command is currently "
|
||||
"unsupported.")
|
||||
# _determine_account will register an account
|
||||
_determine_account(config)
|
||||
return
|
||||
|
||||
# --update-registration
|
||||
if len(accounts) == 0:
|
||||
return "Could not find an existing account to update."
|
||||
if config.email is None:
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ def _translate_ocsp_query(cert_path, ocsp_output, ocsp_errors):
|
|||
logger.info("OCSP revocation warning: %s", warning)
|
||||
return True
|
||||
else:
|
||||
logger.warn("Unable to properly parse OCSP output: %s\nstderr:%s",
|
||||
logger.warning("Unable to properly parse OCSP output: %s\nstderr:%s",
|
||||
ocsp_output, ocsp_errors)
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ class EnhancementTest(test_util.ConfigTestCase):
|
|||
self.assertTrue([i for i in enabled if i["name"] == "somethingelse"])
|
||||
|
||||
def test_are_requested(self):
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
len([i for i in enhancements.enabled_enhancements(self.config)]), 0)
|
||||
self.assertFalse(enhancements.are_requested(self.config))
|
||||
self.config.auto_hsts = True
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
len([i for i in enhancements.enabled_enhancements(self.config)]), 1)
|
||||
self.assertTrue(enhancements.are_requested(self.config))
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ class EnhancementTest(test_util.ConfigTestCase):
|
|||
lineage = "lineage"
|
||||
enhancements.enable(lineage, domains, self.mockinstaller, self.config)
|
||||
self.assertTrue(self.mockinstaller.enable_autohsts.called)
|
||||
self.assertEquals(self.mockinstaller.enable_autohsts.call_args[0],
|
||||
self.assertEqual(self.mockinstaller.enable_autohsts.call_args[0],
|
||||
(lineage, domains))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -197,7 +197,7 @@ class GetUnpreparedInstallerTest(test_util.ConfigTestCase):
|
|||
|
||||
def test_no_installer_defined(self):
|
||||
self.config.configurator = None
|
||||
self.assertEquals(self._call(), None)
|
||||
self.assertEqual(self._call(), None)
|
||||
|
||||
def test_no_available_installers(self):
|
||||
self.config.configurator = "apache"
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ class ServerManagerTest(unittest.TestCase):
|
|||
errors.StandaloneBindError, self.mgr.run, port,
|
||||
challenge_type=challenges.HTTP01)
|
||||
self.assertEqual(self.mgr.running(), {})
|
||||
some_server.close()
|
||||
maybe_another_server.close()
|
||||
|
||||
|
||||
class SupportedChallengesActionTest(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import itertools
|
|||
import logging
|
||||
import os
|
||||
import traceback
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
|
||||
import six
|
||||
import zope.component
|
||||
|
|
@ -276,8 +279,10 @@ def _avoid_invalidating_lineage(config, lineage, original_server):
|
|||
"Do not renew a valid cert with one from a staging server!"
|
||||
# Some lineages may have begun with --staging, but then had production certs
|
||||
# added to them
|
||||
with open(lineage.cert) as the_file:
|
||||
contents = the_file.read()
|
||||
latest_cert = OpenSSL.crypto.load_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, open(lineage.cert).read())
|
||||
OpenSSL.crypto.FILETYPE_PEM, contents)
|
||||
# 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()
|
||||
|
|
@ -370,7 +375,7 @@ def _renew_describe_results(config, renew_successes, renew_failures,
|
|||
disp.notification("\n".join(out), wrap=False)
|
||||
|
||||
|
||||
def handle_renewal_request(config):
|
||||
def handle_renewal_request(config): # pylint: disable=too-many-locals,too-many-branches,too-many-statements
|
||||
"""Examine each lineage; renew if due and report results"""
|
||||
|
||||
# This is trivially False if config.domains is empty
|
||||
|
|
@ -394,6 +399,14 @@ def handle_renewal_request(config):
|
|||
renew_failures = []
|
||||
renew_skipped = []
|
||||
parse_failures = []
|
||||
|
||||
# Noninteractive renewals include a random delay in order to spread
|
||||
# out the load on the certificate authority servers, even if many
|
||||
# users all pick the same time for renewals. This delay precedes
|
||||
# running any hooks, so that side effects of the hooks (such as
|
||||
# shutting down a web service) aren't prolonged unnecessarily.
|
||||
apply_random_sleep = not sys.stdin.isatty() and config.random_sleep_on_renew
|
||||
|
||||
for renewal_file in conf_files:
|
||||
disp = zope.component.getUtility(interfaces.IDisplay)
|
||||
disp.notification("Processing " + renewal_file, pause=False)
|
||||
|
|
@ -422,6 +435,15 @@ def handle_renewal_request(config):
|
|||
from certbot import main
|
||||
plugins = plugins_disco.PluginsRegistry.find_all()
|
||||
if should_renew(lineage_config, renewal_candidate):
|
||||
# Apply random sleep upon first renewal if needed
|
||||
if apply_random_sleep:
|
||||
sleep_time = random.randint(1, 60 * 8)
|
||||
logger.info("Non-interactive renewal: random delay of %s seconds",
|
||||
sleep_time)
|
||||
time.sleep(sleep_time)
|
||||
# We will sleep only once this day, folks.
|
||||
apply_random_sleep = False
|
||||
|
||||
# domains have been restored into lineage_config by reconstitute
|
||||
# but they're unnecessary anyway because renew_cert here
|
||||
# will just grab them from the certificate
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ logger = logging.getLogger(__name__)
|
|||
ALL_FOUR = ("cert", "privkey", "chain", "fullchain")
|
||||
README = "README"
|
||||
CURRENT_VERSION = util.get_strict_version(certbot.__version__)
|
||||
BASE_PRIVKEY_MODE = 0o600
|
||||
|
||||
|
||||
def renewal_conf_files(config):
|
||||
|
|
@ -40,7 +41,9 @@ def renewal_conf_files(config):
|
|||
:rtype: `list` of `str`
|
||||
|
||||
"""
|
||||
return glob.glob(os.path.join(config.renewal_configs_dir, "*.conf"))
|
||||
result = glob.glob(os.path.join(config.renewal_configs_dir, "*.conf"))
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
def renewal_file_for_certname(config, certname):
|
||||
"""Return /path/to/certname.conf in the renewal conf directory"""
|
||||
|
|
@ -792,7 +795,7 @@ class RenewableCert(object):
|
|||
May need to recover from rare interrupted / crashed states."""
|
||||
|
||||
if self.has_pending_deployment():
|
||||
logger.warn("Found a new cert /archive/ that was not linked to in /live/; "
|
||||
logger.warning("Found a new cert /archive/ that was not linked to in /live/; "
|
||||
"fixing...")
|
||||
self.update_all_links_to(self.latest_common_version())
|
||||
return False
|
||||
|
|
@ -1035,9 +1038,11 @@ class RenewableCert(object):
|
|||
archive = full_archive_path(None, cli_config, lineagename)
|
||||
live_dir = _full_live_path(cli_config, lineagename)
|
||||
if os.path.exists(archive):
|
||||
config_file.close()
|
||||
raise errors.CertStorageError(
|
||||
"archive directory exists for " + lineagename)
|
||||
if os.path.exists(live_dir):
|
||||
config_file.close()
|
||||
raise errors.CertStorageError(
|
||||
"live directory exists for " + lineagename)
|
||||
os.mkdir(archive)
|
||||
|
|
@ -1048,13 +1053,14 @@ class RenewableCert(object):
|
|||
# Put the data into the appropriate files on disk
|
||||
target = dict([(kind, os.path.join(live_dir, kind + ".pem"))
|
||||
for kind in ALL_FOUR])
|
||||
archive_target = dict([(kind, os.path.join(archive, kind + "1.pem"))
|
||||
for kind in ALL_FOUR])
|
||||
for kind in ALL_FOUR:
|
||||
os.symlink(os.path.join(_relpath_from_file(archive, target[kind]), kind + "1.pem"),
|
||||
target[kind])
|
||||
os.symlink(_relpath_from_file(archive_target[kind], target[kind]), target[kind])
|
||||
with open(target["cert"], "wb") as f:
|
||||
logger.debug("Writing certificate to %s.", target["cert"])
|
||||
f.write(cert)
|
||||
with open(target["privkey"], "wb") as f:
|
||||
with util.safe_open(archive_target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f:
|
||||
logger.debug("Writing private key to %s.", target["privkey"])
|
||||
f.write(privkey)
|
||||
# XXX: Let's make sure to get the file permissions right here
|
||||
|
|
@ -1118,14 +1124,15 @@ class RenewableCert(object):
|
|||
os.path.join(self.archive_dir, "{0}{1}.pem".format(kind, target_version)))
|
||||
for kind in ALL_FOUR])
|
||||
|
||||
old_privkey = os.path.join(
|
||||
self.archive_dir, "privkey{0}.pem".format(prior_version))
|
||||
|
||||
# Distinguish the cases where the privkey has changed and where it
|
||||
# has not changed (in the latter case, making an appropriate symlink
|
||||
# to an earlier privkey version)
|
||||
if new_privkey is None:
|
||||
# The behavior below keeps the prior key by creating a new
|
||||
# symlink to the old key or the target of the old key symlink.
|
||||
old_privkey = os.path.join(
|
||||
self.archive_dir, "privkey{0}.pem".format(prior_version))
|
||||
if os.path.islink(old_privkey):
|
||||
old_privkey = os.readlink(old_privkey)
|
||||
else:
|
||||
|
|
@ -1133,9 +1140,16 @@ class RenewableCert(object):
|
|||
logger.debug("Writing symlink to old private key, %s.", old_privkey)
|
||||
os.symlink(old_privkey, target["privkey"])
|
||||
else:
|
||||
with open(target["privkey"], "wb") as f:
|
||||
with util.safe_open(target["privkey"], "wb", chmod=BASE_PRIVKEY_MODE) as f:
|
||||
logger.debug("Writing new private key to %s.", target["privkey"])
|
||||
f.write(new_privkey)
|
||||
# Preserve gid and (mode & 074) from previous privkey in this lineage.
|
||||
old_mode = stat.S_IMODE(os.stat(old_privkey).st_mode) & \
|
||||
(stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | \
|
||||
stat.S_IROTH)
|
||||
mode = BASE_PRIVKEY_MODE | old_mode
|
||||
os.chown(target["privkey"], -1, os.stat(old_privkey).st_gid)
|
||||
os.chmod(target["privkey"], mode)
|
||||
|
||||
# Save everything else
|
||||
with open(target["cert"], "wb") as f:
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@ class BaseCertManagerTest(test_util.ConfigTestCase):
|
|||
# We also create a file that isn't a renewal config in the same
|
||||
# location to test that logic that reads in all-and-only renewal
|
||||
# configs will ignore it and NOT attempt to parse it.
|
||||
junk = open(os.path.join(self.config.renewal_configs_dir, "IGNORE.THIS"), "w")
|
||||
junk.write("This file should be ignored!")
|
||||
junk.close()
|
||||
with open(os.path.join(self.config.renewal_configs_dir, "IGNORE.THIS"), "w") as junk:
|
||||
junk.write("This file should be ignored!")
|
||||
|
||||
def _set_up_config(self, domain, custom_archive):
|
||||
# TODO: maybe provide NamespaceConfig.make_dirs?
|
||||
|
|
@ -589,7 +588,7 @@ class GetCertnameTest(unittest.TestCase):
|
|||
from certbot import cert_manager
|
||||
prompt = "Which certificate would you"
|
||||
self.mock_get_utility().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=False), ['example.com'])
|
||||
self.assertTrue(
|
||||
|
|
@ -603,11 +602,11 @@ class GetCertnameTest(unittest.TestCase):
|
|||
from certbot import cert_manager
|
||||
prompt = "custom prompt"
|
||||
self.mock_get_utility().menu.return_value = (display_util.OK, 0)
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=False, custom_prompt=prompt),
|
||||
['example.com'])
|
||||
self.assertEquals(self.mock_get_utility().menu.call_args[0][0],
|
||||
self.assertEqual(self.mock_get_utility().menu.call_args[0][0],
|
||||
prompt)
|
||||
|
||||
@mock.patch('certbot.storage.renewal_conf_files')
|
||||
|
|
@ -631,7 +630,7 @@ class GetCertnameTest(unittest.TestCase):
|
|||
prompt = "Which certificate(s) would you"
|
||||
self.mock_get_utility().checklist.return_value = (display_util.OK,
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=True), ['example.com'])
|
||||
self.assertTrue(
|
||||
|
|
@ -646,11 +645,11 @@ class GetCertnameTest(unittest.TestCase):
|
|||
prompt = "custom prompt"
|
||||
self.mock_get_utility().checklist.return_value = (display_util.OK,
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
cert_manager.get_certnames(
|
||||
self.config, "verb", allow_multiple=True, custom_prompt=prompt),
|
||||
['example.com'])
|
||||
self.assertEquals(
|
||||
self.assertEqual(
|
||||
self.mock_get_utility().checklist.call_args[0][0],
|
||||
prompt)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import unittest
|
|||
import os
|
||||
import tempfile
|
||||
import copy
|
||||
import sys
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
|
@ -41,6 +42,15 @@ class TestReadFile(TempDirTestCase):
|
|||
self.assertEqual(contents, test_contents)
|
||||
|
||||
|
||||
class FlagDefaultTest(unittest.TestCase):
|
||||
"""Tests cli.flag_default"""
|
||||
|
||||
def test_linux_directories(self):
|
||||
if 'fcntl' in sys.modules:
|
||||
self.assertEqual(cli.flag_default('config_dir'), '/etc/letsencrypt')
|
||||
self.assertEqual(cli.flag_default('work_dir'), '/var/lib/letsencrypt')
|
||||
self.assertEqual(cli.flag_default('logs_dir'), '/var/log/letsencrypt')
|
||||
|
||||
|
||||
class ParseTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
||||
'''Test the cli args entrypoint'''
|
||||
|
|
@ -431,6 +441,11 @@ class ParseTest(unittest.TestCase): # pylint: disable=too-many-public-methods
|
|||
self.assertRaises(errors.Error, self.parse,
|
||||
"--allow-subset-of-names -d *.example.org".split())
|
||||
|
||||
def test_route53_no_revert(self):
|
||||
for help_flag in ['-h', '--help']:
|
||||
for topic in ['all', 'plugins', 'dns-route53']:
|
||||
self.assertFalse('certbot-route53:auth' in self._help_output([help_flag, topic]))
|
||||
|
||||
|
||||
class DefaultTest(unittest.TestCase):
|
||||
"""Tests for certbot.cli._Default."""
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ from certbot import util
|
|||
|
||||
import certbot.tests.util as test_util
|
||||
|
||||
from josepy import interfaces
|
||||
|
||||
KEY = test_util.load_vector("rsa512_key.pem")
|
||||
CSR_SAN = test_util.load_vector("csr-san_512.pem")
|
||||
|
||||
|
|
@ -64,9 +66,28 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
tos_cb = mock.MagicMock()
|
||||
return register(self.config, self.account_storage, tos_cb)
|
||||
|
||||
@staticmethod
|
||||
def _public_key_mock():
|
||||
m = mock.Mock(__class__=interfaces.JSONDeSerializable)
|
||||
m.to_partial_json.return_value = '{"a": 1}'
|
||||
return m
|
||||
|
||||
@staticmethod
|
||||
def _new_acct_dir_mock():
|
||||
return "/acme/new-account"
|
||||
|
||||
@staticmethod
|
||||
def _true_mock():
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _false_mock():
|
||||
return False
|
||||
|
||||
def test_no_tos(self):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client.new_account_and_tos().terms_of_service = "http://tos"
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription") as mock_handle:
|
||||
with mock.patch("certbot.account.report_new_account"):
|
||||
mock_client().new_account_and_tos.side_effect = errors.Error
|
||||
|
|
@ -78,7 +99,8 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
self.assertTrue(mock_handle.called)
|
||||
|
||||
def test_it(self):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2"):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.account.report_new_account"):
|
||||
with mock.patch("certbot.eff.handle_subscription"):
|
||||
self._call()
|
||||
|
|
@ -91,6 +113,7 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
|
||||
mx_err = messages.Error.with_code('invalidContact', detail=msg)
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription") as mock_handle:
|
||||
mock_client().new_account_and_tos.side_effect = [mx_err, mock.MagicMock()]
|
||||
self._call()
|
||||
|
|
@ -104,6 +127,7 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
msg = "DNS problem: NXDOMAIN looking up MX for example.com"
|
||||
mx_err = messages.Error.with_code('invalidContact', detail=msg)
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription"):
|
||||
mock_client().new_account_and_tos.side_effect = [mx_err, mock.MagicMock()]
|
||||
self.assertRaises(errors.Error, self._call)
|
||||
|
|
@ -115,7 +139,8 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
@mock.patch("certbot.client.logger")
|
||||
def test_without_email(self, mock_logger):
|
||||
with mock.patch("certbot.eff.handle_subscription") as mock_handle:
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2"):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_clnt:
|
||||
mock_clnt().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.account.report_new_account"):
|
||||
self.config.email = None
|
||||
self.config.register_unsafely_without_email = True
|
||||
|
|
@ -129,6 +154,7 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
def test_dry_run_no_staging_account(self, _rep, mock_get_email):
|
||||
"""Tests dry-run for no staging account, expect account created with no email"""
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription"):
|
||||
with mock.patch("certbot.account.report_new_account"):
|
||||
self.config.dry_run = True
|
||||
|
|
@ -138,11 +164,53 @@ class RegisterTest(test_util.ConfigTestCase):
|
|||
# check Certbot created an account with no email. Contact should return empty
|
||||
self.assertFalse(mock_client().new_account_and_tos.call_args[0][0].contact)
|
||||
|
||||
def test_with_eab_arguments(self):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().client.directory.__getitem__ = mock.Mock(
|
||||
side_effect=self._new_acct_dir_mock
|
||||
)
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription"):
|
||||
target = "certbot.client.messages.ExternalAccountBinding.from_data"
|
||||
with mock.patch(target) as mock_eab_from_data:
|
||||
self.config.eab_kid = "test-kid"
|
||||
self.config.eab_hmac_key = "J2OAqW4MHXsrHVa_PVg0Y-L_R4SYw0_aL1le6mfblbE"
|
||||
self._call()
|
||||
|
||||
self.assertTrue(mock_eab_from_data.called)
|
||||
|
||||
def test_without_eab_arguments(self):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription"):
|
||||
target = "certbot.client.messages.ExternalAccountBinding.from_data"
|
||||
with mock.patch(target) as mock_eab_from_data:
|
||||
self.config.eab_kid = None
|
||||
self.config.eab_hmac_key = None
|
||||
self._call()
|
||||
|
||||
self.assertFalse(mock_eab_from_data.called)
|
||||
|
||||
def test_external_account_required_without_eab_arguments(self):
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().client.net.key.public_key = mock.Mock(side_effect=self._public_key_mock)
|
||||
mock_client().external_account_required.side_effect = self._true_mock
|
||||
with mock.patch("certbot.eff.handle_subscription"):
|
||||
with mock.patch("certbot.client.messages.ExternalAccountBinding.from_data"):
|
||||
self.config.eab_kid = None
|
||||
self.config.eab_hmac_key = None
|
||||
|
||||
self.assertRaises(errors.Error, self._call)
|
||||
|
||||
def test_unsupported_error(self):
|
||||
from acme import messages
|
||||
msg = "Test"
|
||||
mx_err = messages.Error(detail=msg, typ="malformed", title="title")
|
||||
with mock.patch("certbot.client.acme_client.BackwardsCompatibleClientV2") as mock_client:
|
||||
mock_client().client.directory.__getitem__ = mock.Mock(
|
||||
side_effect=self._new_acct_dir_mock
|
||||
)
|
||||
mock_client().external_account_required.side_effect = self._false_mock
|
||||
with mock.patch("certbot.eff.handle_subscription") as mock_handle:
|
||||
mock_client().new_account_and_tos.side_effect = [mx_err, mock.MagicMock()]
|
||||
self.assertRaises(messages.Error, self._call)
|
||||
|
|
@ -487,7 +555,7 @@ class EnhanceConfigTest(ClientTestCommon):
|
|||
self.config.hsts = True
|
||||
self._test_with_already_existing()
|
||||
self.assertTrue(mock_log.warning.called)
|
||||
self.assertEquals(mock_log.warning.call_args[0][1],
|
||||
self.assertEqual(mock_log.warning.call_args[0][1],
|
||||
'Strict-Transport-Security')
|
||||
|
||||
@mock.patch("certbot.client.logger")
|
||||
|
|
@ -495,7 +563,7 @@ class EnhanceConfigTest(ClientTestCommon):
|
|||
self.config.redirect = True
|
||||
self._test_with_already_existing()
|
||||
self.assertTrue(mock_log.warning.called)
|
||||
self.assertEquals(mock_log.warning.call_args[0][1],
|
||||
self.assertEqual(mock_log.warning.call_args[0][1],
|
||||
'redirect')
|
||||
|
||||
def test_no_ask_hsts(self):
|
||||
|
|
|
|||
|
|
@ -502,9 +502,9 @@ class ChooseValuesTest(unittest.TestCase):
|
|||
items = ["first", "second", "third"]
|
||||
mock_util().checklist.return_value = (display_util.OK, [items[2]])
|
||||
result = self._call(items, None)
|
||||
self.assertEquals(result, [items[2]])
|
||||
self.assertEqual(result, [items[2]])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], None)
|
||||
self.assertEqual(mock_util().checklist.call_args[0][0], None)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_success_question(self, mock_util):
|
||||
|
|
@ -512,9 +512,9 @@ class ChooseValuesTest(unittest.TestCase):
|
|||
question = "Which one?"
|
||||
mock_util().checklist.return_value = (display_util.OK, [items[1]])
|
||||
result = self._call(items, question)
|
||||
self.assertEquals(result, [items[1]])
|
||||
self.assertEqual(result, [items[1]])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], question)
|
||||
self.assertEqual(mock_util().checklist.call_args[0][0], question)
|
||||
|
||||
@test_util.patch_get_utility("certbot.display.ops.z_util")
|
||||
def test_choose_names_user_cancel(self, mock_util):
|
||||
|
|
@ -522,9 +522,9 @@ class ChooseValuesTest(unittest.TestCase):
|
|||
question = "Want to cancel?"
|
||||
mock_util().checklist.return_value = (display_util.CANCEL, [])
|
||||
result = self._call(items, question)
|
||||
self.assertEquals(result, [])
|
||||
self.assertEqual(result, [])
|
||||
self.assertTrue(mock_util().checklist.called)
|
||||
self.assertEquals(mock_util().checklist.call_args[0][0], question)
|
||||
self.assertEqual(mock_util().checklist.call_args[0][0], question)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class InputWithTimeoutTest(unittest.TestCase):
|
|||
stdin.listen(1)
|
||||
with mock.patch("certbot.display.util.sys.stdin", stdin):
|
||||
self.assertRaises(errors.Error, self._call, timeout=0.001)
|
||||
stdin.close()
|
||||
|
||||
|
||||
class FileOutputDisplayTest(unittest.TestCase):
|
||||
|
|
@ -314,7 +315,11 @@ class FileOutputDisplayTest(unittest.TestCase):
|
|||
# Every IDisplay method implemented by FileDisplay must take
|
||||
# force_interactive to prevent workflow regressions.
|
||||
for name in interfaces.IDisplay.names(): # pylint: disable=no-member
|
||||
arg_spec = inspect.getargspec(getattr(self.displayer, name))
|
||||
if six.PY2:
|
||||
getargspec = inspect.getargspec # pylint: disable=no-member
|
||||
else:
|
||||
getargspec = inspect.getfullargspec # pylint: disable=no-member
|
||||
arg_spec = getargspec(getattr(self.displayer, name))
|
||||
self.assertTrue("force_interactive" in arg_spec.args)
|
||||
|
||||
|
||||
|
|
@ -371,7 +376,12 @@ class NoninteractiveDisplayTest(unittest.TestCase):
|
|||
for name in interfaces.IDisplay.names(): # pylint: disable=no-member
|
||||
method = getattr(self.displayer, name)
|
||||
# asserts method accepts arbitrary keyword arguments
|
||||
self.assertFalse(inspect.getargspec(method).keywords is None)
|
||||
if six.PY2:
|
||||
result = inspect.getargspec(method).keywords # pylint: disable=no-member
|
||||
self.assertFalse(result is None)
|
||||
else:
|
||||
result = inspect.getfullargspec(method).varkw # pylint: disable=no-member
|
||||
self.assertFalse(result is None)
|
||||
|
||||
|
||||
class SeparateListInputTest(unittest.TestCase):
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class PostArgParseSetupTest(test_util.ConfigTestCase):
|
|||
self.memory_handler.close()
|
||||
self.stream_handler.close()
|
||||
self.temp_handler.close()
|
||||
self.devnull.close()
|
||||
super(PostArgParseSetupTest, self).tearDown()
|
||||
|
||||
def test_common(self):
|
||||
|
|
|
|||
|
|
@ -520,6 +520,8 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
'--work-dir', self.config.work_dir,
|
||||
'--logs-dir', self.config.logs_dir, '--text']
|
||||
|
||||
self.mock_sleep = mock.patch('time.sleep').start()
|
||||
|
||||
def tearDown(self):
|
||||
# Reset globals in cli
|
||||
reload_module(cli)
|
||||
|
|
@ -944,8 +946,8 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
@mock.patch('certbot.crypto_util.notAfter')
|
||||
@test_util.patch_get_utility()
|
||||
def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter):
|
||||
cert_path = '/etc/letsencrypt/live/foo.bar'
|
||||
key_path = '/etc/letsencrypt/live/baz.qux'
|
||||
cert_path = os.path.normpath(os.path.join(self.config.config_dir, 'live/foo.bar'))
|
||||
key_path = os.path.normpath(os.path.join(self.config.config_dir, 'live/baz.qux'))
|
||||
date = '1970-01-01'
|
||||
mock_notAfter().date.return_value = date
|
||||
|
||||
|
|
@ -975,7 +977,8 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
reuse_key=False):
|
||||
# pylint: disable=too-many-locals,too-many-arguments,too-many-branches
|
||||
cert_path = test_util.vector_path('cert_512.pem')
|
||||
chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem'
|
||||
chain_path = os.path.normpath(os.path.join(self.config.config_dir,
|
||||
'live/foo.bar/fullchain.pem'))
|
||||
mock_lineage = mock.MagicMock(cert=cert_path, fullchain=chain_path,
|
||||
cert_path=cert_path, fullchain_path=chain_path)
|
||||
mock_lineage.should_autorenew.return_value = due_for_renewal
|
||||
|
|
@ -1092,6 +1095,26 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
args = ["renew", "--reuse-key"]
|
||||
self._test_renewal_common(True, [], args=args, should_renew=True, reuse_key=True)
|
||||
|
||||
@mock.patch('sys.stdin')
|
||||
def test_noninteractive_renewal_delay(self, stdin):
|
||||
stdin.isatty.return_value = False
|
||||
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
|
||||
args = ["renew", "--dry-run", "-tvv"]
|
||||
self._test_renewal_common(True, [], args=args, should_renew=True)
|
||||
self.assertEqual(self.mock_sleep.call_count, 1)
|
||||
# in main.py:
|
||||
# sleep_time = random.randint(1, 60*8)
|
||||
sleep_call_arg = self.mock_sleep.call_args[0][0]
|
||||
self.assertTrue(1 <= sleep_call_arg <= 60*8)
|
||||
|
||||
@mock.patch('sys.stdin')
|
||||
def test_interactive_no_renewal_delay(self, stdin):
|
||||
stdin.isatty.return_value = True
|
||||
test_util.make_lineage(self.config.config_dir, 'sample-renewal.conf')
|
||||
args = ["renew", "--dry-run", "-tvv"]
|
||||
self._test_renewal_common(True, [], args=args, should_renew=True)
|
||||
self.assertEqual(self.mock_sleep.call_count, 0)
|
||||
|
||||
@mock.patch('certbot.renewal.should_renew')
|
||||
def test_renew_skips_recent_certs(self, should_renew):
|
||||
should_renew.return_value = False
|
||||
|
|
@ -1375,7 +1398,20 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
x = self._call_no_clientmock(["register", "--email", "user@example.org"])
|
||||
self.assertTrue("There is an existing account" in x[0])
|
||||
|
||||
def test_update_registration_no_existing_accounts(self):
|
||||
def test_update_account_no_existing_accounts(self):
|
||||
# with mock.patch('certbot.main.client') as mocked_client:
|
||||
with mock.patch('certbot.main.account') as mocked_account:
|
||||
mocked_storage = mock.MagicMock()
|
||||
mocked_account.AccountFileStorage.return_value = mocked_storage
|
||||
mocked_storage.find_all.return_value = []
|
||||
x = self._call_no_clientmock(
|
||||
["update_account", "--email",
|
||||
"user@example.org"])
|
||||
self.assertTrue("Could not find an existing account" in x[0])
|
||||
|
||||
# TODO: When `certbot register --update-registration` is fully deprecated,
|
||||
# delete the following test
|
||||
def test_update_registration_no_existing_accounts_deprecated(self):
|
||||
# with mock.patch('certbot.main.client') as mocked_client:
|
||||
with mock.patch('certbot.main.account') as mocked_account:
|
||||
mocked_storage = mock.MagicMock()
|
||||
|
|
@ -1386,7 +1422,9 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
"user@example.org"])
|
||||
self.assertTrue("Could not find an existing account" in x[0])
|
||||
|
||||
def test_update_registration_unsafely(self):
|
||||
# TODO: When `certbot register --update-registration` is fully deprecated,
|
||||
# delete the following test
|
||||
def test_update_registration_unsafely_deprecated(self):
|
||||
# This test will become obsolete when register --update-registration
|
||||
# supports removing an e-mail address from the account
|
||||
with mock.patch('certbot.main.account') as mocked_account:
|
||||
|
|
@ -1400,7 +1438,39 @@ class MainTest(test_util.ConfigTestCase): # pylint: disable=too-many-public-met
|
|||
|
||||
@mock.patch('certbot.main.display_ops.get_email')
|
||||
@test_util.patch_get_utility()
|
||||
def test_update_registration_with_email(self, mock_utility, mock_email):
|
||||
def test_update_account_with_email(self, mock_utility, mock_email):
|
||||
email = "user@example.com"
|
||||
mock_email.return_value = email
|
||||
with mock.patch('certbot.eff.handle_subscription') as mock_handle:
|
||||
with mock.patch('certbot.main._determine_account') as mocked_det:
|
||||
with mock.patch('certbot.main.account') as mocked_account:
|
||||
with mock.patch('certbot.main.client') as mocked_client:
|
||||
mocked_storage = mock.MagicMock()
|
||||
mocked_account.AccountFileStorage.return_value = mocked_storage
|
||||
mocked_storage.find_all.return_value = ["an account"]
|
||||
mocked_det.return_value = (mock.MagicMock(), "foo")
|
||||
cb_client = mock.MagicMock()
|
||||
mocked_client.Client.return_value = cb_client
|
||||
x = self._call_no_clientmock(
|
||||
["update_account"])
|
||||
# When registration change succeeds, the return value
|
||||
# of register() is None
|
||||
self.assertTrue(x[0] is None)
|
||||
# and we got supposedly did update the registration from
|
||||
# the server
|
||||
self.assertTrue(
|
||||
cb_client.acme.update_registration.called)
|
||||
# and we saved the updated registration on disk
|
||||
self.assertTrue(mocked_storage.save_regr.called)
|
||||
self.assertTrue(
|
||||
email in mock_utility().add_message.call_args[0][0])
|
||||
self.assertTrue(mock_handle.called)
|
||||
|
||||
# TODO: When `certbot register --update-registration` is fully deprecated,
|
||||
# delete the following test
|
||||
@mock.patch('certbot.main.display_ops.get_email')
|
||||
@test_util.patch_get_utility()
|
||||
def test_update_registration_with_email_deprecated(self, mock_utility, mock_email):
|
||||
email = "user@example.com"
|
||||
mock_email.return_value = email
|
||||
with mock.patch('certbot.eff.handle_subscription') as mock_handle:
|
||||
|
|
@ -1652,7 +1722,7 @@ class EnhanceTest(test_util.ConfigTestCase):
|
|||
mock_lineage.return_value = mock.MagicMock(chain_path="/tmp/nonexistent")
|
||||
self._call(['enhance', '--auto-hsts'])
|
||||
self.assertTrue(self.mockinstaller.enable_autohsts.called)
|
||||
self.assertEquals(self.mockinstaller.enable_autohsts.call_args[0][1],
|
||||
self.assertEqual(self.mockinstaller.enable_autohsts.call_args[0][1],
|
||||
["example.com", "another.tld"])
|
||||
|
||||
@mock.patch('certbot.cert_manager.lineage_for_certname')
|
||||
|
|
|
|||
|
|
@ -96,15 +96,15 @@ class OCSPTest(unittest.TestCase):
|
|||
self.assertEqual(ocsp._translate_ocsp_query(*openssl_happy), False)
|
||||
self.assertEqual(ocsp._translate_ocsp_query(*openssl_confused), False)
|
||||
self.assertEqual(mock_log.debug.call_count, 1)
|
||||
self.assertEqual(mock_log.warn.call_count, 0)
|
||||
self.assertEqual(mock_log.warning.call_count, 0)
|
||||
mock_log.debug.call_count = 0
|
||||
self.assertEqual(ocsp._translate_ocsp_query(*openssl_unknown), False)
|
||||
self.assertEqual(mock_log.debug.call_count, 1)
|
||||
self.assertEqual(mock_log.warn.call_count, 0)
|
||||
self.assertEqual(mock_log.warning.call_count, 0)
|
||||
self.assertEqual(ocsp._translate_ocsp_query(*openssl_expired_ocsp), False)
|
||||
self.assertEqual(mock_log.debug.call_count, 2)
|
||||
self.assertEqual(ocsp._translate_ocsp_query(*openssl_broken), False)
|
||||
self.assertEqual(mock_log.warn.call_count, 1)
|
||||
self.assertEqual(mock_log.warning.call_count, 1)
|
||||
mock_log.info.call_count = 0
|
||||
self.assertEqual(ocsp._translate_ocsp_query(*openssl_revoked), True)
|
||||
self.assertEqual(mock_log.info.call_count, 0)
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
|
|||
self.config.dry_run = True
|
||||
updater.run_generic_updaters(self.config, None, None)
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertEquals(mock_log.call_args[0][0],
|
||||
self.assertEqual(mock_log.call_args[0][0],
|
||||
"Skipping updaters in dry-run mode.")
|
||||
|
||||
@mock.patch("certbot.updater.logger.debug")
|
||||
|
|
@ -61,7 +61,7 @@ class RenewUpdaterTest(test_util.ConfigTestCase):
|
|||
self.config.dry_run = True
|
||||
updater.run_renewal_deployer(self.config, None, None)
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertEquals(mock_log.call_args[0][0],
|
||||
self.assertEqual(mock_log.call_args[0][0],
|
||||
"Skipping renewal deployer in dry-run mode.")
|
||||
|
||||
@mock.patch('certbot.plugins.selection.get_unprepared_installer')
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import six
|
|||
|
||||
import certbot
|
||||
from certbot import cli
|
||||
from certbot import compat
|
||||
from certbot import errors
|
||||
from certbot.storage import ALL_FOUR
|
||||
|
||||
|
|
@ -73,9 +74,8 @@ class BaseRenewableCertTest(test_util.ConfigTestCase):
|
|||
# We also create a file that isn't a renewal config in the same
|
||||
# location to test that logic that reads in all-and-only renewal
|
||||
# configs will ignore it and NOT attempt to parse it.
|
||||
junk = open(os.path.join(self.config.config_dir, "renewal", "IGNORE.THIS"), "w")
|
||||
junk.write("This file should be ignored!")
|
||||
junk.close()
|
||||
with open(os.path.join(self.config.config_dir, "renewal", "IGNORE.THIS"), "w") as junk:
|
||||
junk.write("This file should be ignored!")
|
||||
|
||||
self.defaults = configobj.ConfigObj()
|
||||
|
||||
|
|
@ -92,6 +92,8 @@ class BaseRenewableCertTest(test_util.ConfigTestCase):
|
|||
link)
|
||||
with open(link, "wb") as f:
|
||||
f.write(kind.encode('ascii') if value is None else value)
|
||||
if kind == "privkey":
|
||||
os.chmod(link, 0o600)
|
||||
|
||||
def _write_out_ex_kinds(self):
|
||||
for kind in ALL_FOUR:
|
||||
|
|
@ -264,12 +266,12 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||
mock_has_pending.return_value = False
|
||||
self.assertEqual(self.test_rc.ensure_deployed(), True)
|
||||
self.assertEqual(mock_update.call_count, 0)
|
||||
self.assertEqual(mock_logger.warn.call_count, 0)
|
||||
self.assertEqual(mock_logger.warning.call_count, 0)
|
||||
|
||||
mock_has_pending.return_value = True
|
||||
self.assertEqual(self.test_rc.ensure_deployed(), False)
|
||||
self.assertEqual(mock_update.call_count, 1)
|
||||
self.assertEqual(mock_logger.warn.call_count, 1)
|
||||
self.assertEqual(mock_logger.warning.call_count, 1)
|
||||
|
||||
|
||||
def test_update_link_to(self):
|
||||
|
|
@ -544,6 +546,47 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||
self.assertFalse(os.path.islink(self.test_rc.version("privkey", 10)))
|
||||
self.assertFalse(os.path.exists(temp_config_file))
|
||||
|
||||
@test_util.broken_on_windows
|
||||
@mock.patch("certbot.storage.relevant_values")
|
||||
def test_save_successor_maintains_group_mode(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 kind in ALL_FOUR:
|
||||
self._write_out_kind(kind, 1)
|
||||
self.test_rc.update_all_links_to(1)
|
||||
self.assertTrue(compat.compare_file_modes(
|
||||
os.stat(self.test_rc.version("privkey", 1)).st_mode, 0o600))
|
||||
os.chmod(self.test_rc.version("privkey", 1), 0o444)
|
||||
# If no new key, permissions should be the same (we didn't write any keys)
|
||||
self.test_rc.save_successor(1, b"newcert", None, b"new chain", self.config)
|
||||
self.assertTrue(compat.compare_file_modes(
|
||||
os.stat(self.test_rc.version("privkey", 2)).st_mode, 0o444))
|
||||
# If new key, permissions should be kept as 644
|
||||
self.test_rc.save_successor(2, b"newcert", b"new_privkey", b"new chain", self.config)
|
||||
self.assertTrue(compat.compare_file_modes(
|
||||
os.stat(self.test_rc.version("privkey", 3)).st_mode, 0o644))
|
||||
# If permissions reverted, next renewal will also revert permissions of new key
|
||||
os.chmod(self.test_rc.version("privkey", 3), 0o400)
|
||||
self.test_rc.save_successor(3, b"newcert", b"new_privkey", b"new chain", self.config)
|
||||
self.assertTrue(compat.compare_file_modes(
|
||||
os.stat(self.test_rc.version("privkey", 4)).st_mode, 0o600))
|
||||
|
||||
@test_util.broken_on_windows
|
||||
@mock.patch("certbot.storage.relevant_values")
|
||||
@mock.patch("certbot.storage.os.chown")
|
||||
def test_save_successor_maintains_gid(self, mock_chown, 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 kind in ALL_FOUR:
|
||||
self._write_out_kind(kind, 1)
|
||||
self.test_rc.update_all_links_to(1)
|
||||
self.test_rc.save_successor(1, b"newcert", None, b"new chain", self.config)
|
||||
self.assertFalse(mock_chown.called)
|
||||
self.test_rc.save_successor(2, b"newcert", b"new_privkey", b"new chain", self.config)
|
||||
self.assertTrue(mock_chown.called)
|
||||
|
||||
def _test_relevant_values_common(self, values):
|
||||
defaults = dict((option, cli.flag_default(option))
|
||||
for option in ("authenticator", "installer",
|
||||
|
|
@ -630,6 +673,7 @@ class RenewableCertTests(BaseRenewableCertTest):
|
|||
self.config.live_dir, "README")))
|
||||
self.assertTrue(os.path.exists(os.path.join(
|
||||
self.config.live_dir, "the-lineage.com", "README")))
|
||||
self.assertTrue(compat.compare_file_modes(os.stat(result.key_path).st_mode, 0o600))
|
||||
with open(result.fullchain, "rb") as f:
|
||||
self.assertEqual(f.read(), b"cert" + b"chain")
|
||||
# Let's do it again and make sure it makes a different lineage
|
||||
|
|
|
|||
|
|
@ -210,16 +210,21 @@ class UniqueFileTest(test_util.TempDirTestCase):
|
|||
fd, name = self._call()
|
||||
fd.write("bar")
|
||||
fd.close()
|
||||
self.assertEqual(open(name).read(), "bar")
|
||||
with open(name) as f:
|
||||
self.assertEqual(f.read(), "bar")
|
||||
|
||||
def test_right_mode(self):
|
||||
self.assertTrue(compat.compare_file_modes(0o700, os.stat(self._call(0o700)[1]).st_mode))
|
||||
self.assertTrue(compat.compare_file_modes(0o600, os.stat(self._call(0o600)[1]).st_mode))
|
||||
fd1, name1 = self._call(0o700)
|
||||
fd2, name2 = self._call(0o600)
|
||||
self.assertTrue(compat.compare_file_modes(0o700, os.stat(name1).st_mode))
|
||||
self.assertTrue(compat.compare_file_modes(0o600, os.stat(name2).st_mode))
|
||||
fd1.close()
|
||||
fd2.close()
|
||||
|
||||
def test_default_exists(self):
|
||||
name1 = self._call()[1] # create 0000_foo.txt
|
||||
name2 = self._call()[1]
|
||||
name3 = self._call()[1]
|
||||
fd1, name1 = self._call() # create 0000_foo.txt
|
||||
fd2, name2 = self._call()
|
||||
fd3, name3 = self._call()
|
||||
|
||||
self.assertNotEqual(name1, name2)
|
||||
self.assertNotEqual(name1, name3)
|
||||
|
|
@ -236,6 +241,10 @@ class UniqueFileTest(test_util.TempDirTestCase):
|
|||
basename3 = os.path.basename(name3)
|
||||
self.assertTrue(basename3.endswith("foo.txt"))
|
||||
|
||||
fd1.close()
|
||||
fd2.close()
|
||||
fd3.close()
|
||||
|
||||
|
||||
try:
|
||||
file_type = file
|
||||
|
|
@ -255,13 +264,18 @@ class UniqueLineageNameTest(test_util.TempDirTestCase):
|
|||
f, path = self._call("wow")
|
||||
self.assertTrue(isinstance(f, file_type))
|
||||
self.assertEqual(os.path.join(self.tempdir, "wow.conf"), path)
|
||||
f.close()
|
||||
|
||||
def test_multiple(self):
|
||||
items = []
|
||||
for _ in six.moves.range(10):
|
||||
f, name = self._call("wow")
|
||||
items.append(self._call("wow"))
|
||||
f, name = items[-1]
|
||||
self.assertTrue(isinstance(f, file_type))
|
||||
self.assertTrue(isinstance(name, six.string_types))
|
||||
self.assertTrue("wow-0009.conf" in name)
|
||||
for f, _ in items:
|
||||
f.close()
|
||||
|
||||
@mock.patch("certbot.util.os.fdopen")
|
||||
def test_failure(self, mock_fdopen):
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ Challenges
|
|||
|
||||
To receive a certificate from Let's Encrypt certificate authority (CA), you must pass a *challenge* to
|
||||
prove you control each of the domain names that will be listed in the certificate. A challenge is one of
|
||||
three tasks that only someone who controls the domain should be able to accomplish:
|
||||
a list of specified tasks that only someone who controls the domain should be able to accomplish, such as:
|
||||
|
||||
* Posting a specified file in a specified location on a web site (the HTTP-01 challenge)
|
||||
* Offering a specified temporary certificate on a web site (the TLS-SNI-01 challenge)
|
||||
* Posting a specified DNS record in the domain name system (the DNS-01 challenge)
|
||||
|
||||
It’s possible to complete each type of challenge *automatically* (Certbot directly makes the necessary
|
||||
|
|
@ -16,21 +15,21 @@ design favors performing challenges automatically, and this is the normal case f
|
|||
|
||||
Some plugins offer an *authenticator*, meaning that they can satisfy challenges:
|
||||
|
||||
* Apache plugin: (TLS-SNI-01) Tries to edit your Apache configuration files to temporarily serve
|
||||
a Certbot-generated certificate for a specified name. Use the Apache plugin when you're running
|
||||
Certbot on a web server with Apache listening on port 443.
|
||||
* NGINX plugin: (TLS-SNI-01) Tries to edit your NGINX configuration files to temporarily serve a
|
||||
Certbot-generated certificate for a specified name. Use the NGINX plugin when you're running
|
||||
Certbot on a web server with NGINX listening on port 443.
|
||||
* Apache plugin: (HTTP-01) Tries to edit your Apache configuration files to temporarily serve files to
|
||||
satisfy challenges from the certificate authority. Use the Apache plugin when you're running Certbot on a
|
||||
web server with Apache listening on port 80.
|
||||
* Nginx plugin: (HTTP-01) Tries to edit your nginx configuration files to temporarily serve files to
|
||||
satisfy challenges from the certificate authority. Use the nginx plugin when you're running Certbot on a
|
||||
web server with nginx listening on port 80.
|
||||
* Webroot plugin: (HTTP-01) Tries to place a file where it can be served over HTTP on port 80 by a
|
||||
web server running on your system. Use the Webroot plugin when you're running Certbot on
|
||||
a web server with any server application listening on port 80 serving files from a folder on disk in response.
|
||||
* Standalone plugin: (TLS-SNI-01 or HTTP-01) Tries to run a temporary web server listening on either HTTP on
|
||||
port 80 (for HTTP-01) or HTTPS on port 443 (for TLS-SNI-01). Use the Standalone plugin if no existing program
|
||||
is listening to these ports. Choose TLS-SNI-01 or HTTP-01 using the `--preferred-challenges` option.
|
||||
* Standalone plugin: (HTTP-01) Tries to run a temporary web server listening on HTTP on port 80. Use the
|
||||
Standalone plugin if no existing program is listening to this port.
|
||||
* Manual plugin: (DNS-01 or HTTP-01) Either tells you what changes to make to your configuration or updates
|
||||
your DNS records using an external script (for DNS-01) or your webroot (for HTTP-01). Use the Manual
|
||||
plugin if you have the technical knowledge to make configuration changes yourself when asked to do so.
|
||||
plugin if you have the technical knowledge to make configuration changes yourself when asked to do so,
|
||||
and are prepared to repeat these steps every time the certificate needs to be renewed.
|
||||
|
||||
Tips for Challenges
|
||||
-------------------
|
||||
|
|
@ -63,20 +62,6 @@ HTTP-01 Challenge
|
|||
* When using the Standalone plugin, make sure another program is not already listening to port 80 on the server.
|
||||
* When using the Webroot plugin, make sure there is a web server listening on port 80.
|
||||
|
||||
TLS-SNI-01 Challenge
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* The TLS-SNI-01 challenge doesn’t work with content delivery networks (CDNs)
|
||||
like CloudFlare and Akamai because the domain name is pointed at the CDN, not directly at your server.
|
||||
* Make sure port 443 is open, publicly reachable from the Internet, and not blocked by a router or firewall.
|
||||
* When using the Apache plugin, make sure you are running Apache and no other web server on port 443.
|
||||
* When using the NGINX plugin, make sure you are running NGINX and no other web server on port 443.
|
||||
* With either the Apache or NGINX plugin, certbot modifies your web server configuration. If you get
|
||||
an error after successfully completing the challenge, then you have received a certificate but the
|
||||
plugin was unable to modify your web server configuration, meaning that you'll have to install the certificate manually.
|
||||
In that case, please file a bug to help us improve certbot!
|
||||
* When using the Standalone plugin, make sure another program is not already listening to port 443 on the server.
|
||||
|
||||
DNS-01 Challenge
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ manage certificates:
|
|||
|
||||
manage your account with Let's Encrypt:
|
||||
register Create a Let's Encrypt ACME account
|
||||
update_account Update a Let's Encrypt ACME account
|
||||
--agree-tos Agree to the ACME server's Subscriber Agreement
|
||||
-m EMAIL Email address for important account notifications
|
||||
|
||||
|
|
@ -67,6 +68,10 @@ optional arguments:
|
|||
with the same name. In the case of a name collision it
|
||||
will append a number like 0001 to the file path name.
|
||||
(default: Ask)
|
||||
--eab-kid EAB_KID Key Identifier for External Account Binding (default:
|
||||
None)
|
||||
--eab-hmac-key EAB_HMAC_KEY
|
||||
HMAC key for External Account Binding (default: None)
|
||||
--cert-name CERTNAME Certificate name to apply. This name is used by
|
||||
Certbot for housekeeping and in file paths; it doesn't
|
||||
affect the content of the certificate itself. To see
|
||||
|
|
@ -108,7 +113,7 @@ optional arguments:
|
|||
case, and to know when to deprecate support for past
|
||||
Python versions and flags. If you wish to hide this
|
||||
information from the Let's Encrypt server, set this to
|
||||
"". (default: CertbotACMEClient/0.28.0
|
||||
"". (default: CertbotACMEClient/0.30.0
|
||||
(certbot(-auto); OS_NAME OS_VERSION) Authenticator/XXX
|
||||
Installer/YYY (SUBCOMMAND; flags: FLAGS)
|
||||
Py/major.minor.patchlevel). The flags encoded in the
|
||||
|
|
@ -355,7 +360,7 @@ revoke:
|
|||
certificates. (default: None)
|
||||
|
||||
register:
|
||||
Options for account registration & modification
|
||||
Options for account registration
|
||||
|
||||
--register-unsafely-without-email
|
||||
Specifying this flag enables registering an account
|
||||
|
|
@ -367,11 +372,6 @@ register:
|
|||
to the Subscriber Agreement will still affect you, and
|
||||
will be effective 14 days after posting an update to
|
||||
the web site. (default: False)
|
||||
--update-registration
|
||||
With the register verb, indicates that details
|
||||
associated with an existing registration, such as the
|
||||
e-mail address, should be updated, rather than
|
||||
registering a new account. (default: False)
|
||||
-m EMAIL, --email EMAIL
|
||||
Email used for registration and recovery contact. Use
|
||||
comma to register multiple emails, ex:
|
||||
|
|
@ -380,6 +380,9 @@ register:
|
|||
--no-eff-email Don't share your e-mail address with EFF (default:
|
||||
None)
|
||||
|
||||
update_account:
|
||||
Options for account modification
|
||||
|
||||
unregister:
|
||||
Options for account deactivation.
|
||||
|
||||
|
|
@ -473,12 +476,13 @@ plugins:
|
|||
using Sakura Cloud for DNS). (default: False)
|
||||
|
||||
apache:
|
||||
Apache Web Server plugin - Beta
|
||||
Apache Web Server plugin
|
||||
|
||||
--apache-enmod APACHE_ENMOD
|
||||
Path to the Apache 'a2enmod' binary (default: None)
|
||||
Path to the Apache 'a2enmod' binary (default: a2enmod)
|
||||
--apache-dismod APACHE_DISMOD
|
||||
Path to the Apache 'a2dismod' binary (default: None)
|
||||
Path to the Apache 'a2dismod' binary (default:
|
||||
a2dismod)
|
||||
--apache-le-vhost-ext APACHE_LE_VHOST_EXT
|
||||
SSL vhost configuration extension (default: -le-
|
||||
ssl.conf)
|
||||
|
|
@ -492,25 +496,16 @@ apache:
|
|||
/var/log/apache2)
|
||||
--apache-challenge-location APACHE_CHALLENGE_LOCATION
|
||||
Directory path for challenge configuration (default:
|
||||
/etc/apache2/other)
|
||||
/etc/apache2)
|
||||
--apache-handle-modules APACHE_HANDLE_MODULES
|
||||
Let installer handle enabling required modules for you
|
||||
(Only Ubuntu/Debian currently) (default: False)
|
||||
(Only Ubuntu/Debian currently) (default: True)
|
||||
--apache-handle-sites APACHE_HANDLE_SITES
|
||||
Let installer handle enabling sites for you (Only
|
||||
Ubuntu/Debian currently) (default: False)
|
||||
Ubuntu/Debian currently) (default: True)
|
||||
--apache-ctl APACHE_CTL
|
||||
Full path to Apache control script (default:
|
||||
apachectl)
|
||||
|
||||
certbot-route53:auth:
|
||||
Obtain certificates using a DNS TXT record (if you are using AWS Route53
|
||||
for DNS).
|
||||
|
||||
--certbot-route53:auth-propagation-seconds CERTBOT_ROUTE53:AUTH_PROPAGATION_SECONDS
|
||||
The number of seconds to wait for DNS to propagate
|
||||
before asking the ACME server to verify the DNS
|
||||
record. (default: 10)
|
||||
apache2ctl)
|
||||
|
||||
dns-cloudflare:
|
||||
Obtain certificates using a DNS TXT record (if you are using Cloudflare
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import os
|
|||
import re
|
||||
import sys
|
||||
|
||||
import sphinx
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
|
@ -33,14 +35,13 @@ sys.path.insert(0, os.path.abspath(os.path.join(here, '..')))
|
|||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
needs_sphinx = '1.0'
|
||||
needs_sphinx = '1.2'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.imgconverter',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
|
|
@ -48,6 +49,9 @@ extensions = [
|
|||
'repoze.sphinx.autointerface',
|
||||
]
|
||||
|
||||
if sphinx.version_info >= (1, 6):
|
||||
extensions.append('sphinx.ext.imgconverter')
|
||||
|
||||
autodoc_member_order = 'bysource'
|
||||
autodoc_default_flags = ['show-inheritance', 'private-members']
|
||||
|
||||
|
|
|
|||
|
|
@ -186,8 +186,8 @@ Authenticators
|
|||
--------------
|
||||
|
||||
Authenticators are plugins that prove control of a domain name by solving a
|
||||
challenge provided by the ACME server. ACME currently defines three types of
|
||||
challenges: HTTP, TLS-SNI, and DNS, represented by classes in `acme.challenges`.
|
||||
challenge provided by the ACME server. ACME currently defines several types of
|
||||
challenges: HTTP, TLS-SNI (deprecated), TLS-ALPR, and DNS, represented by classes in `acme.challenges`.
|
||||
An authenticator plugin should implement support for at least one challenge type.
|
||||
|
||||
An Authenticator indicates which challenges it supports by implementing
|
||||
|
|
@ -215,7 +215,7 @@ 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
|
||||
that listens on port 80, but it cannot install certs; a postfix plugin would
|
||||
be an installer but not an authenticator).
|
||||
|
||||
Installers and Authenticators are kept separate because
|
||||
|
|
@ -359,7 +359,10 @@ Steps:
|
|||
4. Run ``tox --skip-missing-interpreters`` to run the entire test suite
|
||||
including coverage. The ``--skip-missing-interpreters`` argument ignores
|
||||
missing versions of Python needed for running the tests. Fix any errors.
|
||||
5. Submit the PR.
|
||||
5. Submit the PR. Once your PR is open, please do not force push to the branch
|
||||
containing your pull request to squash or amend commits. We use `squash
|
||||
merges <https://github.com/blog/2141-squash-your-commits>`_ on PRs and
|
||||
rewriting commits makes changes harder to track between reviews.
|
||||
6. Did your tests pass on Travis? If they didn't, fix any errors.
|
||||
|
||||
Asking for help
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ System Requirements
|
|||
Certbot currently requires Python 2.7 or 3.4+ running on a UNIX-like operating
|
||||
system. By default, it 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
|
||||
bind to port 80 (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
|
||||
|
|
|
|||
|
|
@ -44,14 +44,13 @@ a combination_ of distinct authenticator and installer plugins.
|
|||
=========== ==== ==== =============================================================== =============================
|
||||
Plugin Auth Inst Notes Challenge types (and port)
|
||||
=========== ==== ==== =============================================================== =============================
|
||||
apache_ Y Y | Automates obtaining and installing a certificate with Apache tls-sni-01_ (443)
|
||||
apache_ Y Y | Automates obtaining and installing a certificate with Apache http-01_ (80)
|
||||
| 2.4 on OSes with ``libaugeas0`` 1.0+.
|
||||
nginx_ Y Y | Automates obtaining and installing a certificate with Nginx. http-01_ (80)
|
||||
webroot_ Y N | Obtains a certificate by writing to the webroot directory of http-01_ (80)
|
||||
| an already running webserver.
|
||||
nginx_ Y Y | Automates obtaining and installing a certificate with Nginx. tls-sni-01_ (443)
|
||||
| Shipped with Certbot 0.9.0.
|
||||
standalone_ Y N | Uses a "standalone" webserver to obtain a certificate. http-01_ (80) or
|
||||
| Requires port 80 or 443 to be available. This is useful on tls-sni-01_ (443)
|
||||
standalone_ Y N | Uses a "standalone" webserver to obtain a certificate. http-01_ (80)
|
||||
| Requires port 80 to be available. This is useful on
|
||||
| systems with no webserver, or when direct integration with
|
||||
| the local webserver is not supported or not desired.
|
||||
|dns_plugs| Y N | This category of plugins automates obtaining a certificate by dns-01_ (53)
|
||||
|
|
@ -59,17 +58,17 @@ standalone_ Y N | Uses a "standalone" webserver to obtain a certificate.
|
|||
| domain. Doing domain validation in this way is
|
||||
| the only way to obtain wildcard certificates from Let's
|
||||
| Encrypt.
|
||||
manual_ Y N | Helps you obtain a certificate by giving you instructions to http-01_ (80),
|
||||
| perform domain validation yourself. Additionally allows you dns-01_ (53) or
|
||||
| to specify scripts to automate the validation task in a tls-sni-01_ (443)
|
||||
manual_ Y N | Helps you obtain a certificate by giving you instructions to http-01_ (80) or
|
||||
| perform domain validation yourself. Additionally allows you dns-01_ (53)
|
||||
| to specify scripts to automate the validation task in a
|
||||
| customized way.
|
||||
=========== ==== ==== =============================================================== =============================
|
||||
|
||||
.. |dns_plugs| replace:: :ref:`DNS plugins <dns_plugins>`
|
||||
|
||||
Under the hood, plugins use one of several ACME protocol challenges_ to
|
||||
prove you control a domain. The options are http-01_ (which uses port 80),
|
||||
tls-sni-01_ (port 443) and dns-01_ (requiring configuration of a DNS server on
|
||||
prove you control a domain. The options are http-01_ (which uses port 80)
|
||||
and dns-01_ (requiring configuration of a DNS server on
|
||||
port 53, though that's often not the same machine as your webserver). A few
|
||||
plugins support more than one challenge type, in which case you can choose one
|
||||
with ``--preferred-challenges``.
|
||||
|
|
@ -78,7 +77,6 @@ There are also many third-party-plugins_ available. Below we describe in more de
|
|||
the circumstances in which each plugin can be used, and how to use it.
|
||||
|
||||
.. _challenges: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7
|
||||
.. _tls-sni-01: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.3
|
||||
.. _http-01: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.2
|
||||
.. _dns-01: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.4
|
||||
|
||||
|
|
@ -159,13 +157,9 @@ software running on the machine where you obtain the certificate.
|
|||
|
||||
To obtain a certificate using a "standalone" webserver, you can use the
|
||||
standalone plugin by including ``certonly`` and ``--standalone``
|
||||
on the command line. This plugin needs to bind to port 80 or 443 in
|
||||
on the command line. This plugin needs to bind to port 80 in
|
||||
order to perform domain validation, so you may need to stop your
|
||||
existing webserver. To control which port the plugin uses, include
|
||||
one of the options shown below on the command line.
|
||||
|
||||
* ``--preferred-challenges http`` to use port 80
|
||||
* ``--preferred-challenges tls-sni`` to use port 443
|
||||
existing webserver.
|
||||
|
||||
It must still be possible for your machine to accept inbound connections from
|
||||
the Internet on the specified port using each requested domain name.
|
||||
|
|
@ -222,8 +216,7 @@ the UI, you can use the plugin to obtain a certificate by specifying
|
|||
to copy and paste commands into another terminal session, which may
|
||||
be on a different computer.
|
||||
|
||||
The manual plugin can use either the ``http``, ``dns`` or the
|
||||
``tls-sni`` challenge. You can use the ``--preferred-challenges`` option
|
||||
The manual plugin can use either the ``http`` or the ``dns`` challenge. You can use the ``--preferred-challenges`` option
|
||||
to choose the challenge of your preference.
|
||||
|
||||
The ``http`` challenge will ask you to place a file with a specific name and
|
||||
|
|
@ -241,11 +234,6 @@ For example, for the domain ``example.com``, a zone file entry would look like:
|
|||
|
||||
_acme-challenge.example.com. 300 IN TXT "gfj9Xq...Rg85nM"
|
||||
|
||||
When using the ``tls-sni`` challenge, ``certbot`` will prepare a self-signed
|
||||
SSL certificate for you with the challenge validation appropriately
|
||||
encoded into a subjectAlternatNames entry. You will need to configure
|
||||
your SSL server to present this challenge SSL certificate to the ACME
|
||||
server using SNI.
|
||||
|
||||
Additionally you can specify scripts to prepare for validation and
|
||||
perform the authentication procedure and/or clean up after it by using
|
||||
|
|
@ -262,16 +250,20 @@ installer plugins. To do so, specify the authenticator plugin with
|
|||
``--authenticator`` or ``-a`` and the installer plugin with ``--installer`` or
|
||||
``-i``.
|
||||
|
||||
For instance, you may want to create a certificate using the webroot_ plugin
|
||||
for authentication and the apache_ plugin for installation, perhaps because you
|
||||
use a proxy or CDN for SSL and only want to secure the connection between them
|
||||
and your origin server, which cannot use the tls-sni-01_ challenge due to the
|
||||
intermediate proxy.
|
||||
For instance, you could create a certificate using the webroot_ plugin
|
||||
for authentication and the apache_ plugin for installation.
|
||||
|
||||
::
|
||||
|
||||
certbot run -a webroot -i apache -w /var/www/html -d example.com
|
||||
|
||||
Or you could create a certificate using the manual_ plugin for authentication
|
||||
and the nginx_ plugin for installation. (Note that this certificate cannot
|
||||
be renewed automatically.)
|
||||
|
||||
::
|
||||
certbot run -a manual -i nginx -d example.com
|
||||
|
||||
.. _third-party-plugins:
|
||||
|
||||
Third-party plugins
|
||||
|
|
@ -696,7 +688,9 @@ Where are my certificates?
|
|||
==========================
|
||||
|
||||
All generated keys and issued certificates can be found in
|
||||
``/etc/letsencrypt/live/$domain``. Rather than copying, please point
|
||||
``/etc/letsencrypt/live/$domain``. In the case of creating a SAN certificate
|
||||
with multiple alternative names, ``$domain`` is the first domain passed in
|
||||
via -d parameter. Rather than copying, please point
|
||||
your (web) server configuration directly to those files (or create
|
||||
symlinks). During the renewal_, ``/etc/letsencrypt/live`` is updated
|
||||
with the latest necessary files.
|
||||
|
|
@ -715,6 +709,10 @@ The following files are available:
|
|||
put it into a safe, however - your server still needs to access
|
||||
this file in order for SSL/TLS to work.
|
||||
|
||||
.. note:: As of Certbot version 0.29.0, private keys for new certificate
|
||||
default to ``0600``. Any changes to the group mode or group owner (gid)
|
||||
of this file will be preserved on renewals.
|
||||
|
||||
This is what Apache needs for `SSLCertificateKeyFile
|
||||
<https://httpd.apache.org/docs/2.4/mod/mod_ssl.html#sslcertificatekeyfile>`_,
|
||||
and Nginx for `ssl_certificate_key
|
||||
|
|
@ -775,9 +773,6 @@ variables to these scripts:
|
|||
- ``CERTBOT_DOMAIN``: The domain being authenticated
|
||||
- ``CERTBOT_VALIDATION``: The validation string (HTTP-01 and DNS-01 only)
|
||||
- ``CERTBOT_TOKEN``: Resource name part of the HTTP-01 challenge (HTTP-01 only)
|
||||
- ``CERTBOT_CERT_PATH``: The challenge SSL certificate (TLS-SNI-01 only)
|
||||
- ``CERTBOT_KEY_PATH``: The private key associated with the aforementioned SSL certificate (TLS-SNI-01 only)
|
||||
- ``CERTBOT_SNI_DOMAIN``: The SNI name for which the ACME server expects to be presented the self-signed certificate located at ``$CERTBOT_CERT_PATH`` (TLS-SNI-01 only)
|
||||
|
||||
Additionally for cleanup:
|
||||
|
||||
|
|
|
|||
154
letsencrypt-auto
154
letsencrypt-auto
|
|
@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then
|
|||
fi
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt"
|
||||
LE_AUTO_VERSION="0.28.0"
|
||||
LE_AUTO_VERSION="0.30.0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
|
|
@ -593,8 +593,7 @@ BootstrapArchCommon() {
|
|||
# - ArchLinux (x86_64)
|
||||
#
|
||||
# "python-virtualenv" is Python3, but "python2-virtualenv" provides
|
||||
# only "virtualenv2" binary, not "virtualenv" necessary in
|
||||
# ./tools/_venv_common.sh
|
||||
# only "virtualenv2" binary, not "virtualenv".
|
||||
|
||||
deps="
|
||||
python2
|
||||
|
|
@ -912,6 +911,35 @@ OldVenvExists() {
|
|||
[ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ]
|
||||
}
|
||||
|
||||
# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2.
|
||||
# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated
|
||||
# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1
|
||||
# is outdated, and "UP_TO_DATE" if not.
|
||||
# This function relies only on installed python environment (2.x or 3.x) by certbot-auto.
|
||||
CompareVersions() {
|
||||
"$1" - "$2" "$3" << "UNLIKELY_EOF"
|
||||
import sys
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
try:
|
||||
current = StrictVersion(sys.argv[1])
|
||||
except ValueError:
|
||||
sys.stdout.write('UNOFFICIAL')
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
remote = StrictVersion(sys.argv[2])
|
||||
except ValueError:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
sys.exit()
|
||||
|
||||
if current < remote:
|
||||
sys.stdout.write('OUTDATED')
|
||||
else:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
UNLIKELY_EOF
|
||||
}
|
||||
|
||||
if [ "$1" = "--le-auto-phase2" ]; then
|
||||
# Phase 2: Create venv, install LE, and run.
|
||||
|
||||
|
|
@ -1017,43 +1045,39 @@ pycparser==2.14 \
|
|||
asn1crypto==0.22.0 \
|
||||
--hash=sha256:d232509fefcfcdb9a331f37e9c9dc20441019ad927c7d2176cf18ed5da0ba097 \
|
||||
--hash=sha256:cbbadd640d3165ab24b06ef25d1dca09a3441611ac15f6a6b452474fdf0aed1a
|
||||
cffi==1.10.0 \
|
||||
--hash=sha256:446699c10f3c390633d0722bc19edbc7ac4b94761918a4a4f7908a24e86ebbd0 \
|
||||
--hash=sha256:562326fc7f55a59ef3fef5e82908fe938cdc4bbda32d734c424c7cd9ed73e93a \
|
||||
--hash=sha256:7f732ad4a30db0b39400c3f7011249f7d0701007d511bf09604729aea222871f \
|
||||
--hash=sha256:94fb8410c6c4fc48e7ea759d3d1d9ca561171a88d00faddd4aa0306f698ad6a0 \
|
||||
--hash=sha256:587a5043df4b00a2130e09fed42da02a4ed3c688bd9bf07a3ac89d2271f4fb07 \
|
||||
--hash=sha256:ec08b88bef627ec1cea210e1608c85d3cf44893bcde74e41b7f7dbdfd2c1bad6 \
|
||||
--hash=sha256:a41406f6d62abcdf3eef9fd998d8dcff04fd2a7746644143045feeebd76352d1 \
|
||||
--hash=sha256:b560916546b2f209d74b82bdbc3223cee9a165b0242fa00a06dfc48a2054864a \
|
||||
--hash=sha256:e74896774e437f4715c57edeb5cf3d3a40d7727f541c2c12156617b5a15d1829 \
|
||||
--hash=sha256:9a31c18ba4881a116e448c52f3f5d3e14401cf7a9c43cc88f06f2a7f5428da0e \
|
||||
--hash=sha256:80796ea68e11624a0279d3b802f88a7fe7214122b97a15a6c97189934a2cc776 \
|
||||
--hash=sha256:f4019826a2dec066c909a1f483ef0dcf9325d6740cc0bd15308942b28b0930f7 \
|
||||
--hash=sha256:7248506981eeba23888b4140a69a53c4c0c0a386abcdca61ed8dd790a73e64b9 \
|
||||
--hash=sha256:a8955265d146e86fe2ce116394be4eaf0cb40314a79b19f11c4fa574cd639572 \
|
||||
--hash=sha256:c49187260043bd4c1d6a52186f9774f17d9b1da0a406798ebf4bfc12da166ade \
|
||||
--hash=sha256:c1d8b3d8dcb5c23ac1a8bf56422036f3f305a3c5a8bc8c354256579a1e2aa2c1 \
|
||||
--hash=sha256:9e389615bcecb8c782a87939d752340bb0a3a097e90bae54d7f0915bc12f45bd \
|
||||
--hash=sha256:d09ff358f75a874f69fa7d1c2b4acecf4282a950293fcfcf89aa606da8a9a500 \
|
||||
--hash=sha256:b69b4557aae7de18b7c174a917fe19873529d927ac592762d9771661875bbd40 \
|
||||
--hash=sha256:5de52b081a2775e76b971de9d997d85c4457fc0a09079e12d66849548ae60981 \
|
||||
--hash=sha256:e7d88fecb7b6250a1fd432e6dc64890342c372fce13dbfe4bb6f16348ad00c14 \
|
||||
--hash=sha256:1426e67e855ef7f5030c9184f4f1a9f4bfa020c31c962cd41fd129ec5aef4a6a \
|
||||
--hash=sha256:267dd2c66a5760c5f4d47e2ebcf8eeac7ef01e1ae6ae7a6d0d241a290068bc38 \
|
||||
--hash=sha256:e553eb489511cacf19eda6e52bc9e151316f0d721724997dda2c4d3079b778db \
|
||||
--hash=sha256:98b89b2c57f97ce2db7aeba60db173c84871d73b40e41a11ea95de1500ddc57e \
|
||||
--hash=sha256:e2b7e090188833bc58b2ae03fb864c22688654ebd2096bcf38bc860c4f38a3d8 \
|
||||
--hash=sha256:afa7d8b8d38ad40db8713ee053d41b36d87d6ae5ec5ad36f9210b548a18dc214 \
|
||||
--hash=sha256:4fc9c2ff7924b3a1fa326e1799e5dd58cac585d7fb25fe53ccaa1333b0453d65 \
|
||||
--hash=sha256:937db39a1ec5af3003b16357b2042bba67c88d43bc11aaa203fa8a5924524209 \
|
||||
--hash=sha256:ab22285797631df3b513b2cd3ecdc51cd8e3d36788e3991d93d0759d6883b027 \
|
||||
--hash=sha256:96e599b924ef009aa867f725b3249ee51d76489f484d3a45b4bd219c5ec6ed59 \
|
||||
--hash=sha256:bea842a0512be6a8007e585790bccd5d530520fc025ce63b03e139be373b0063 \
|
||||
--hash=sha256:e7175287f7fe7b1cc203bb958b17db40abd732690c1e18e700f10e0843a58598 \
|
||||
--hash=sha256:285ab352552f52f1398c912556d4d36d4ea9b8450e5c65d03809bf9886755533 \
|
||||
--hash=sha256:5576644b859197da7bbd8f8c7c2fb5dcc6cd505cadb42992d5f104c013f8a214 \
|
||||
--hash=sha256:b3b02911eb1f6ada203b0763ba924234629b51586f72a21faacc638269f4ced5
|
||||
cffi==1.11.5 \
|
||||
--hash=sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50 \
|
||||
--hash=sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596 \
|
||||
--hash=sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef \
|
||||
--hash=sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743 \
|
||||
--hash=sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f \
|
||||
--hash=sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31 \
|
||||
--hash=sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04 \
|
||||
--hash=sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6 \
|
||||
--hash=sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3 \
|
||||
--hash=sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6 \
|
||||
--hash=sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b \
|
||||
--hash=sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca \
|
||||
--hash=sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e \
|
||||
--hash=sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb \
|
||||
--hash=sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd \
|
||||
--hash=sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1 \
|
||||
--hash=sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917 \
|
||||
--hash=sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359 \
|
||||
--hash=sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f \
|
||||
--hash=sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95 \
|
||||
--hash=sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801 \
|
||||
--hash=sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257 \
|
||||
--hash=sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184 \
|
||||
--hash=sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc \
|
||||
--hash=sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085 \
|
||||
--hash=sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93 \
|
||||
--hash=sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2 \
|
||||
--hash=sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30 \
|
||||
--hash=sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5 \
|
||||
--hash=sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e \
|
||||
--hash=sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b \
|
||||
--hash=sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4
|
||||
ConfigArgParse==0.12.0 \
|
||||
--hash=sha256:28cd7d67669651f2a4518367838c49539457504584a139709b2b8f6c208ef339 \
|
||||
--no-binary ConfigArgParse
|
||||
|
|
@ -1146,9 +1170,9 @@ pytz==2015.7 \
|
|||
--hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \
|
||||
--hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \
|
||||
--hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3
|
||||
requests==2.12.1 \
|
||||
--hash=sha256:3f3f27a9d0f9092935efc78054ef324eb9f8166718270aefe036dfa1e4f68e1e \
|
||||
--hash=sha256:2109ecea94df90980be040490ff1d879971b024861539abb00054062388b612e
|
||||
requests==2.20.0 \
|
||||
--hash=sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c \
|
||||
--hash=sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279
|
||||
six==1.10.0 \
|
||||
--hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
|
||||
--hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
|
||||
|
|
@ -1185,6 +1209,15 @@ zope.interface==4.1.3 \
|
|||
requests-toolbelt==0.8.0 \
|
||||
--hash=sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237 \
|
||||
--hash=sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5
|
||||
chardet==3.0.2 \
|
||||
--hash=sha256:4f7832e7c583348a9eddd927ee8514b3bf717c061f57b21dbe7697211454d9bb \
|
||||
--hash=sha256:6ebf56457934fdce01fb5ada5582762a84eed94cad43ed877964aebbdd8174c0
|
||||
urllib3==1.24.1 \
|
||||
--hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \
|
||||
--hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22
|
||||
certifi==2017.4.17 \
|
||||
--hash=sha256:f4318671072f030a33c7ca6acaef720ddd50ff124d1388e50c1bda4cbd6d7010 \
|
||||
--hash=sha256:f7527ebf7461582ce95f7a9e03dd141ce810d40590834f4ec20cddd54234c10a
|
||||
|
||||
# Contains the requirements for the letsencrypt package.
|
||||
#
|
||||
|
|
@ -1197,31 +1230,29 @@ letsencrypt==0.7.0 \
|
|||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.28.0 \
|
||||
--hash=sha256:f2f7c816acd695cbcda713a779b0db2b08e9de407146b46e1c4ef5561e0f5417 \
|
||||
--hash=sha256:31e3e2ee2a25c009a621c59ac9182f85d937a897c7bd1d47d0e01f3c712a090a
|
||||
acme==0.28.0 \
|
||||
--hash=sha256:d3a564031155fece3f6edce8c5246d4cf34be0977b4c7c5ce84e86c68601c895 \
|
||||
--hash=sha256:bf7c2f1c24a26ab5b9fce3a6abca1d74a5914d46919649ae00ad5817db62bb85
|
||||
certbot-apache==0.28.0 \
|
||||
--hash=sha256:a57d7bac4f13ae5ecea4f4cbd479d381c02316c4832b25ab5a9d7c6826166370 \
|
||||
--hash=sha256:3f93f5de4a548e973c493a6cac5eeeb3dbbcae2988b61299ea0727d04a00f5bb
|
||||
certbot-nginx==0.28.0 \
|
||||
--hash=sha256:1822a65910f0801087fa20a3af3fc94f878f93e0f11809483bb5387df861e296 \
|
||||
--hash=sha256:426fb403b0a7b203629f4e350a862cbc3bc1f69936fdab8ec7eafe0d8a3b5ddb
|
||||
certbot==0.30.0 \
|
||||
--hash=sha256:b3468e128e74d2295598f6d3fbf9d0edfb67fe5abaca3b985a9e858395bd027f \
|
||||
--hash=sha256:d631fe6c75700ce9b2fdae194ff8b53c7518545d87dd451a1704f7572dcd49e8
|
||||
acme==0.30.0 \
|
||||
--hash=sha256:eed9389f802ebf4988c9e43c28ad3d5c2734237371d78e97450a1d61189a15aa \
|
||||
--hash=sha256:984b6d00bec73dcfa616636a760e80ca14bd246fb908710a656547f542f09445
|
||||
certbot-apache==0.30.0 \
|
||||
--hash=sha256:d38c70fc6930db298ea992a3145362eebdce460d3d2651f86a8f2f43d838c6d0 \
|
||||
--hash=sha256:1d4bc207d53a3e5d37e5d9ebd05f26089aa21d1fbf384113ed9d1829b4d1e9bf
|
||||
certbot-nginx==0.30.0 \
|
||||
--hash=sha256:6163c7d0080f59b4ebe510afcc6af2d2eebf15469275c3835866690db4d465d6 \
|
||||
--hash=sha256:e39a3f3d77cd4c653949cf066fb2211039fd2032665697c27b6e8501c7c2dd92
|
||||
|
||||
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
|
||||
|
|
@ -1348,10 +1379,8 @@ def hashed_download(url, temp, digest):
|
|||
|
||||
def get_index_base():
|
||||
"""Return the URL to the dir containing the "packages" folder.
|
||||
|
||||
Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the
|
||||
end if it's there; that is likely to give us the right dir.
|
||||
|
||||
"""
|
||||
env_var = environ.get('PIP_INDEX_URL', '').rstrip('/')
|
||||
if env_var:
|
||||
|
|
@ -1641,7 +1670,12 @@ UNLIKELY_EOF
|
|||
error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates."
|
||||
elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
error "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
fi
|
||||
|
||||
LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"`
|
||||
if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then
|
||||
say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION"
|
||||
elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then
|
||||
say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlvjV5wACgkQTRfJlc2X
|
||||
dfKkRwf+MJ/Yo5ix7rxGMoliJl3GUUC2KvuYxObvbsAZW69Zl4aZVNeUP3Pe/EZj
|
||||
zJlSMuiCPeTMmmr0+q78dk5Qk0vf+9D5qSQyy2U+RvPvX6z1PfaFXwjETwOEhE4i
|
||||
7pABP4m/rIhlZbh336gou4XZK8sXsKHXBLQEyqmzPm6YFZ+5vowIoEinrN73PBuq
|
||||
rgvoTFKi2NTjYNkQffYUeCIgO0pXlaOa8hkaupqoejHHEjjiXS2C9m0gAT2Wk2cO
|
||||
zya5WQNcCCLWy/ChhPE2M7yRSpwqrszsHP0qo7QGL8vvsdXvNeJ7vwpAlq/9aipg
|
||||
PpzSXy/ek8YAgApaj8+/w4OfdDhQ4Q==
|
||||
=1hD2
|
||||
iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAlwtH9cACgkQTRfJlc2X
|
||||
dfIqUwf/RXLZAeFF/59PjTAzcV+eEISlvEmFcV0zL3vv23PsY3S5Iuuwcd6rTm5M
|
||||
UWNtmUTmFVo0xmxAj6Eqfpnt0P+JPpPcnbLNIGKFekBWIshgH84RRFWPJjNh/hu1
|
||||
pyzkkcWaOB86egdVfjvuRJ0j7AGd0ih6ur2rlgfHVjTYR+0EdWszFDEFBlq8cpct
|
||||
9d1gCgH7VWKSIQMhzGLMsmdMxNoDl4hiqVPU0FP5/mn2xGF7FgeKNW3+NiTouKuB
|
||||
mZOeEl3f3uOze/suHPyfOu+49jk+TWWE05Xfqfowjf486nKPg6/uSA2izW/MwIKN
|
||||
HuIuY3bBf+lx5yUVIraoZhH2MxODDQ==
|
||||
=BZqz
|
||||
-----END PGP SIGNATURE-----
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ if [ -z "$VENV_PATH" ]; then
|
|||
fi
|
||||
VENV_BIN="$VENV_PATH/bin"
|
||||
BOOTSTRAP_VERSION_PATH="$VENV_PATH/certbot-auto-bootstrap-version.txt"
|
||||
LE_AUTO_VERSION="0.29.0.dev0"
|
||||
LE_AUTO_VERSION="0.31.0.dev0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
|
|
@ -593,8 +593,7 @@ BootstrapArchCommon() {
|
|||
# - ArchLinux (x86_64)
|
||||
#
|
||||
# "python-virtualenv" is Python3, but "python2-virtualenv" provides
|
||||
# only "virtualenv2" binary, not "virtualenv" necessary in
|
||||
# ./tools/_venv_common.py
|
||||
# only "virtualenv2" binary, not "virtualenv".
|
||||
|
||||
deps="
|
||||
python2
|
||||
|
|
@ -912,6 +911,35 @@ OldVenvExists() {
|
|||
[ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ]
|
||||
}
|
||||
|
||||
# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2.
|
||||
# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated
|
||||
# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1
|
||||
# is outdated, and "UP_TO_DATE" if not.
|
||||
# This function relies only on installed python environment (2.x or 3.x) by certbot-auto.
|
||||
CompareVersions() {
|
||||
"$1" - "$2" "$3" << "UNLIKELY_EOF"
|
||||
import sys
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
try:
|
||||
current = StrictVersion(sys.argv[1])
|
||||
except ValueError:
|
||||
sys.stdout.write('UNOFFICIAL')
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
remote = StrictVersion(sys.argv[2])
|
||||
except ValueError:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
sys.exit()
|
||||
|
||||
if current < remote:
|
||||
sys.stdout.write('OUTDATED')
|
||||
else:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
UNLIKELY_EOF
|
||||
}
|
||||
|
||||
if [ "$1" = "--le-auto-phase2" ]; then
|
||||
# Phase 2: Create venv, install LE, and run.
|
||||
|
||||
|
|
@ -1017,43 +1045,39 @@ pycparser==2.14 \
|
|||
asn1crypto==0.22.0 \
|
||||
--hash=sha256:d232509fefcfcdb9a331f37e9c9dc20441019ad927c7d2176cf18ed5da0ba097 \
|
||||
--hash=sha256:cbbadd640d3165ab24b06ef25d1dca09a3441611ac15f6a6b452474fdf0aed1a
|
||||
cffi==1.10.0 \
|
||||
--hash=sha256:446699c10f3c390633d0722bc19edbc7ac4b94761918a4a4f7908a24e86ebbd0 \
|
||||
--hash=sha256:562326fc7f55a59ef3fef5e82908fe938cdc4bbda32d734c424c7cd9ed73e93a \
|
||||
--hash=sha256:7f732ad4a30db0b39400c3f7011249f7d0701007d511bf09604729aea222871f \
|
||||
--hash=sha256:94fb8410c6c4fc48e7ea759d3d1d9ca561171a88d00faddd4aa0306f698ad6a0 \
|
||||
--hash=sha256:587a5043df4b00a2130e09fed42da02a4ed3c688bd9bf07a3ac89d2271f4fb07 \
|
||||
--hash=sha256:ec08b88bef627ec1cea210e1608c85d3cf44893bcde74e41b7f7dbdfd2c1bad6 \
|
||||
--hash=sha256:a41406f6d62abcdf3eef9fd998d8dcff04fd2a7746644143045feeebd76352d1 \
|
||||
--hash=sha256:b560916546b2f209d74b82bdbc3223cee9a165b0242fa00a06dfc48a2054864a \
|
||||
--hash=sha256:e74896774e437f4715c57edeb5cf3d3a40d7727f541c2c12156617b5a15d1829 \
|
||||
--hash=sha256:9a31c18ba4881a116e448c52f3f5d3e14401cf7a9c43cc88f06f2a7f5428da0e \
|
||||
--hash=sha256:80796ea68e11624a0279d3b802f88a7fe7214122b97a15a6c97189934a2cc776 \
|
||||
--hash=sha256:f4019826a2dec066c909a1f483ef0dcf9325d6740cc0bd15308942b28b0930f7 \
|
||||
--hash=sha256:7248506981eeba23888b4140a69a53c4c0c0a386abcdca61ed8dd790a73e64b9 \
|
||||
--hash=sha256:a8955265d146e86fe2ce116394be4eaf0cb40314a79b19f11c4fa574cd639572 \
|
||||
--hash=sha256:c49187260043bd4c1d6a52186f9774f17d9b1da0a406798ebf4bfc12da166ade \
|
||||
--hash=sha256:c1d8b3d8dcb5c23ac1a8bf56422036f3f305a3c5a8bc8c354256579a1e2aa2c1 \
|
||||
--hash=sha256:9e389615bcecb8c782a87939d752340bb0a3a097e90bae54d7f0915bc12f45bd \
|
||||
--hash=sha256:d09ff358f75a874f69fa7d1c2b4acecf4282a950293fcfcf89aa606da8a9a500 \
|
||||
--hash=sha256:b69b4557aae7de18b7c174a917fe19873529d927ac592762d9771661875bbd40 \
|
||||
--hash=sha256:5de52b081a2775e76b971de9d997d85c4457fc0a09079e12d66849548ae60981 \
|
||||
--hash=sha256:e7d88fecb7b6250a1fd432e6dc64890342c372fce13dbfe4bb6f16348ad00c14 \
|
||||
--hash=sha256:1426e67e855ef7f5030c9184f4f1a9f4bfa020c31c962cd41fd129ec5aef4a6a \
|
||||
--hash=sha256:267dd2c66a5760c5f4d47e2ebcf8eeac7ef01e1ae6ae7a6d0d241a290068bc38 \
|
||||
--hash=sha256:e553eb489511cacf19eda6e52bc9e151316f0d721724997dda2c4d3079b778db \
|
||||
--hash=sha256:98b89b2c57f97ce2db7aeba60db173c84871d73b40e41a11ea95de1500ddc57e \
|
||||
--hash=sha256:e2b7e090188833bc58b2ae03fb864c22688654ebd2096bcf38bc860c4f38a3d8 \
|
||||
--hash=sha256:afa7d8b8d38ad40db8713ee053d41b36d87d6ae5ec5ad36f9210b548a18dc214 \
|
||||
--hash=sha256:4fc9c2ff7924b3a1fa326e1799e5dd58cac585d7fb25fe53ccaa1333b0453d65 \
|
||||
--hash=sha256:937db39a1ec5af3003b16357b2042bba67c88d43bc11aaa203fa8a5924524209 \
|
||||
--hash=sha256:ab22285797631df3b513b2cd3ecdc51cd8e3d36788e3991d93d0759d6883b027 \
|
||||
--hash=sha256:96e599b924ef009aa867f725b3249ee51d76489f484d3a45b4bd219c5ec6ed59 \
|
||||
--hash=sha256:bea842a0512be6a8007e585790bccd5d530520fc025ce63b03e139be373b0063 \
|
||||
--hash=sha256:e7175287f7fe7b1cc203bb958b17db40abd732690c1e18e700f10e0843a58598 \
|
||||
--hash=sha256:285ab352552f52f1398c912556d4d36d4ea9b8450e5c65d03809bf9886755533 \
|
||||
--hash=sha256:5576644b859197da7bbd8f8c7c2fb5dcc6cd505cadb42992d5f104c013f8a214 \
|
||||
--hash=sha256:b3b02911eb1f6ada203b0763ba924234629b51586f72a21faacc638269f4ced5
|
||||
cffi==1.11.5 \
|
||||
--hash=sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50 \
|
||||
--hash=sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596 \
|
||||
--hash=sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef \
|
||||
--hash=sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743 \
|
||||
--hash=sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f \
|
||||
--hash=sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31 \
|
||||
--hash=sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04 \
|
||||
--hash=sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6 \
|
||||
--hash=sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3 \
|
||||
--hash=sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6 \
|
||||
--hash=sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b \
|
||||
--hash=sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca \
|
||||
--hash=sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e \
|
||||
--hash=sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb \
|
||||
--hash=sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd \
|
||||
--hash=sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1 \
|
||||
--hash=sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917 \
|
||||
--hash=sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359 \
|
||||
--hash=sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f \
|
||||
--hash=sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95 \
|
||||
--hash=sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801 \
|
||||
--hash=sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257 \
|
||||
--hash=sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184 \
|
||||
--hash=sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc \
|
||||
--hash=sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085 \
|
||||
--hash=sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93 \
|
||||
--hash=sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2 \
|
||||
--hash=sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30 \
|
||||
--hash=sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5 \
|
||||
--hash=sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e \
|
||||
--hash=sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b \
|
||||
--hash=sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4
|
||||
ConfigArgParse==0.12.0 \
|
||||
--hash=sha256:28cd7d67669651f2a4518367838c49539457504584a139709b2b8f6c208ef339 \
|
||||
--no-binary ConfigArgParse
|
||||
|
|
@ -1146,9 +1170,9 @@ pytz==2015.7 \
|
|||
--hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \
|
||||
--hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \
|
||||
--hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3
|
||||
requests==2.12.1 \
|
||||
--hash=sha256:3f3f27a9d0f9092935efc78054ef324eb9f8166718270aefe036dfa1e4f68e1e \
|
||||
--hash=sha256:2109ecea94df90980be040490ff1d879971b024861539abb00054062388b612e
|
||||
requests==2.20.0 \
|
||||
--hash=sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c \
|
||||
--hash=sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279
|
||||
six==1.10.0 \
|
||||
--hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
|
||||
--hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
|
||||
|
|
@ -1185,6 +1209,15 @@ zope.interface==4.1.3 \
|
|||
requests-toolbelt==0.8.0 \
|
||||
--hash=sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237 \
|
||||
--hash=sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5
|
||||
chardet==3.0.2 \
|
||||
--hash=sha256:4f7832e7c583348a9eddd927ee8514b3bf717c061f57b21dbe7697211454d9bb \
|
||||
--hash=sha256:6ebf56457934fdce01fb5ada5582762a84eed94cad43ed877964aebbdd8174c0
|
||||
urllib3==1.24.1 \
|
||||
--hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \
|
||||
--hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22
|
||||
certifi==2017.4.17 \
|
||||
--hash=sha256:f4318671072f030a33c7ca6acaef720ddd50ff124d1388e50c1bda4cbd6d7010 \
|
||||
--hash=sha256:f7527ebf7461582ce95f7a9e03dd141ce810d40590834f4ec20cddd54234c10a
|
||||
|
||||
# Contains the requirements for the letsencrypt package.
|
||||
#
|
||||
|
|
@ -1197,31 +1230,29 @@ letsencrypt==0.7.0 \
|
|||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.28.0 \
|
||||
--hash=sha256:f2f7c816acd695cbcda713a779b0db2b08e9de407146b46e1c4ef5561e0f5417 \
|
||||
--hash=sha256:31e3e2ee2a25c009a621c59ac9182f85d937a897c7bd1d47d0e01f3c712a090a
|
||||
acme==0.28.0 \
|
||||
--hash=sha256:d3a564031155fece3f6edce8c5246d4cf34be0977b4c7c5ce84e86c68601c895 \
|
||||
--hash=sha256:bf7c2f1c24a26ab5b9fce3a6abca1d74a5914d46919649ae00ad5817db62bb85
|
||||
certbot-apache==0.28.0 \
|
||||
--hash=sha256:a57d7bac4f13ae5ecea4f4cbd479d381c02316c4832b25ab5a9d7c6826166370 \
|
||||
--hash=sha256:3f93f5de4a548e973c493a6cac5eeeb3dbbcae2988b61299ea0727d04a00f5bb
|
||||
certbot-nginx==0.28.0 \
|
||||
--hash=sha256:1822a65910f0801087fa20a3af3fc94f878f93e0f11809483bb5387df861e296 \
|
||||
--hash=sha256:426fb403b0a7b203629f4e350a862cbc3bc1f69936fdab8ec7eafe0d8a3b5ddb
|
||||
certbot==0.30.0 \
|
||||
--hash=sha256:b3468e128e74d2295598f6d3fbf9d0edfb67fe5abaca3b985a9e858395bd027f \
|
||||
--hash=sha256:d631fe6c75700ce9b2fdae194ff8b53c7518545d87dd451a1704f7572dcd49e8
|
||||
acme==0.30.0 \
|
||||
--hash=sha256:eed9389f802ebf4988c9e43c28ad3d5c2734237371d78e97450a1d61189a15aa \
|
||||
--hash=sha256:984b6d00bec73dcfa616636a760e80ca14bd246fb908710a656547f542f09445
|
||||
certbot-apache==0.30.0 \
|
||||
--hash=sha256:d38c70fc6930db298ea992a3145362eebdce460d3d2651f86a8f2f43d838c6d0 \
|
||||
--hash=sha256:1d4bc207d53a3e5d37e5d9ebd05f26089aa21d1fbf384113ed9d1829b4d1e9bf
|
||||
certbot-nginx==0.30.0 \
|
||||
--hash=sha256:6163c7d0080f59b4ebe510afcc6af2d2eebf15469275c3835866690db4d465d6 \
|
||||
--hash=sha256:e39a3f3d77cd4c653949cf066fb2211039fd2032665697c27b6e8501c7c2dd92
|
||||
|
||||
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
|
||||
|
|
@ -1260,7 +1291,7 @@ except ImportError:
|
|||
cmd = popenargs[0]
|
||||
raise CalledProcessError(retcode, cmd)
|
||||
return output
|
||||
from sys import exit, version_info, executable
|
||||
from sys import exit, version_info
|
||||
from tempfile import mkdtemp
|
||||
try:
|
||||
from urllib2 import build_opener, HTTPHandler, HTTPSHandler
|
||||
|
|
@ -1272,7 +1303,7 @@ except ImportError:
|
|||
from urllib.parse import urlparse # 3.4
|
||||
|
||||
|
||||
__version__ = 2, 0, 0
|
||||
__version__ = 1, 5, 1
|
||||
PIP_VERSION = '9.0.1'
|
||||
DEFAULT_INDEX_BASE = 'https://pypi.python.org'
|
||||
|
||||
|
|
@ -1348,10 +1379,8 @@ def hashed_download(url, temp, digest):
|
|||
|
||||
def get_index_base():
|
||||
"""Return the URL to the dir containing the "packages" folder.
|
||||
|
||||
Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the
|
||||
end if it's there; that is likely to give us the right dir.
|
||||
|
||||
"""
|
||||
env_var = environ.get('PIP_INDEX_URL', '').rstrip('/')
|
||||
if env_var:
|
||||
|
|
@ -1365,7 +1394,7 @@ def get_index_base():
|
|||
|
||||
|
||||
def main():
|
||||
pip_version = StrictVersion(check_output([executable, '-m', 'pip', '--version'])
|
||||
pip_version = StrictVersion(check_output(['pip', '--version'])
|
||||
.decode('utf-8').split()[1])
|
||||
min_pip_version = StrictVersion(PIP_VERSION)
|
||||
if pip_version >= min_pip_version:
|
||||
|
|
@ -1378,7 +1407,7 @@ def main():
|
|||
temp,
|
||||
digest)
|
||||
for path, digest in PACKAGES]
|
||||
check_output('{0} -m pip install --no-index --no-deps -U '.format(quote(executable)) +
|
||||
check_output('pip install --no-index --no-deps -U ' +
|
||||
# Disable cache since we're not using it and it otherwise
|
||||
# sometimes throws permission warnings:
|
||||
('--no-cache-dir ' if has_pip_cache else '') +
|
||||
|
|
@ -1397,6 +1426,7 @@ def main():
|
|||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
# Set PATH so pipstrap upgrades the right (v)env:
|
||||
|
|
@ -1640,7 +1670,12 @@ UNLIKELY_EOF
|
|||
error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates."
|
||||
elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
error "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
fi
|
||||
|
||||
LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"`
|
||||
if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then
|
||||
say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION"
|
||||
elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then
|
||||
say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -451,6 +451,35 @@ OldVenvExists() {
|
|||
[ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ]
|
||||
}
|
||||
|
||||
# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2.
|
||||
# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated
|
||||
# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1
|
||||
# is outdated, and "UP_TO_DATE" if not.
|
||||
# This function relies only on installed python environment (2.x or 3.x) by certbot-auto.
|
||||
CompareVersions() {
|
||||
"$1" - "$2" "$3" << "UNLIKELY_EOF"
|
||||
import sys
|
||||
from distutils.version import StrictVersion
|
||||
|
||||
try:
|
||||
current = StrictVersion(sys.argv[1])
|
||||
except ValueError:
|
||||
sys.stdout.write('UNOFFICIAL')
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
remote = StrictVersion(sys.argv[2])
|
||||
except ValueError:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
sys.exit()
|
||||
|
||||
if current < remote:
|
||||
sys.stdout.write('OUTDATED')
|
||||
else:
|
||||
sys.stdout.write('UP_TO_DATE')
|
||||
UNLIKELY_EOF
|
||||
}
|
||||
|
||||
if [ "$1" = "--le-auto-phase2" ]; then
|
||||
# Phase 2: Create venv, install LE, and run.
|
||||
|
||||
|
|
@ -635,7 +664,12 @@ UNLIKELY_EOF
|
|||
error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates."
|
||||
elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then
|
||||
error "WARNING: unable to check for updates."
|
||||
elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
|
||||
fi
|
||||
|
||||
LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"`
|
||||
if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then
|
||||
say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION"
|
||||
elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then
|
||||
say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
|
||||
|
||||
# Now we drop into Python so we don't have to install even more
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ BootstrapArchCommon() {
|
|||
# - ArchLinux (x86_64)
|
||||
#
|
||||
# "python-virtualenv" is Python3, but "python2-virtualenv" provides
|
||||
# only "virtualenv2" binary, not "virtualenv" necessary in
|
||||
# ./tools/_venv_common.py
|
||||
# only "virtualenv2" binary, not "virtualenv".
|
||||
|
||||
deps="
|
||||
python2
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
certbot==0.28.0 \
|
||||
--hash=sha256:f2f7c816acd695cbcda713a779b0db2b08e9de407146b46e1c4ef5561e0f5417 \
|
||||
--hash=sha256:31e3e2ee2a25c009a621c59ac9182f85d937a897c7bd1d47d0e01f3c712a090a
|
||||
acme==0.28.0 \
|
||||
--hash=sha256:d3a564031155fece3f6edce8c5246d4cf34be0977b4c7c5ce84e86c68601c895 \
|
||||
--hash=sha256:bf7c2f1c24a26ab5b9fce3a6abca1d74a5914d46919649ae00ad5817db62bb85
|
||||
certbot-apache==0.28.0 \
|
||||
--hash=sha256:a57d7bac4f13ae5ecea4f4cbd479d381c02316c4832b25ab5a9d7c6826166370 \
|
||||
--hash=sha256:3f93f5de4a548e973c493a6cac5eeeb3dbbcae2988b61299ea0727d04a00f5bb
|
||||
certbot-nginx==0.28.0 \
|
||||
--hash=sha256:1822a65910f0801087fa20a3af3fc94f878f93e0f11809483bb5387df861e296 \
|
||||
--hash=sha256:426fb403b0a7b203629f4e350a862cbc3bc1f69936fdab8ec7eafe0d8a3b5ddb
|
||||
certbot==0.30.0 \
|
||||
--hash=sha256:b3468e128e74d2295598f6d3fbf9d0edfb67fe5abaca3b985a9e858395bd027f \
|
||||
--hash=sha256:d631fe6c75700ce9b2fdae194ff8b53c7518545d87dd451a1704f7572dcd49e8
|
||||
acme==0.30.0 \
|
||||
--hash=sha256:eed9389f802ebf4988c9e43c28ad3d5c2734237371d78e97450a1d61189a15aa \
|
||||
--hash=sha256:984b6d00bec73dcfa616636a760e80ca14bd246fb908710a656547f542f09445
|
||||
certbot-apache==0.30.0 \
|
||||
--hash=sha256:d38c70fc6930db298ea992a3145362eebdce460d3d2651f86a8f2f43d838c6d0 \
|
||||
--hash=sha256:1d4bc207d53a3e5d37e5d9ebd05f26089aa21d1fbf384113ed9d1829b4d1e9bf
|
||||
certbot-nginx==0.30.0 \
|
||||
--hash=sha256:6163c7d0080f59b4ebe510afcc6af2d2eebf15469275c3835866690db4d465d6 \
|
||||
--hash=sha256:e39a3f3d77cd4c653949cf066fb2211039fd2032665697c27b6e8501c7c2dd92
|
||||
|
|
|
|||
|
|
@ -21,43 +21,39 @@ pycparser==2.14 \
|
|||
asn1crypto==0.22.0 \
|
||||
--hash=sha256:d232509fefcfcdb9a331f37e9c9dc20441019ad927c7d2176cf18ed5da0ba097 \
|
||||
--hash=sha256:cbbadd640d3165ab24b06ef25d1dca09a3441611ac15f6a6b452474fdf0aed1a
|
||||
cffi==1.10.0 \
|
||||
--hash=sha256:446699c10f3c390633d0722bc19edbc7ac4b94761918a4a4f7908a24e86ebbd0 \
|
||||
--hash=sha256:562326fc7f55a59ef3fef5e82908fe938cdc4bbda32d734c424c7cd9ed73e93a \
|
||||
--hash=sha256:7f732ad4a30db0b39400c3f7011249f7d0701007d511bf09604729aea222871f \
|
||||
--hash=sha256:94fb8410c6c4fc48e7ea759d3d1d9ca561171a88d00faddd4aa0306f698ad6a0 \
|
||||
--hash=sha256:587a5043df4b00a2130e09fed42da02a4ed3c688bd9bf07a3ac89d2271f4fb07 \
|
||||
--hash=sha256:ec08b88bef627ec1cea210e1608c85d3cf44893bcde74e41b7f7dbdfd2c1bad6 \
|
||||
--hash=sha256:a41406f6d62abcdf3eef9fd998d8dcff04fd2a7746644143045feeebd76352d1 \
|
||||
--hash=sha256:b560916546b2f209d74b82bdbc3223cee9a165b0242fa00a06dfc48a2054864a \
|
||||
--hash=sha256:e74896774e437f4715c57edeb5cf3d3a40d7727f541c2c12156617b5a15d1829 \
|
||||
--hash=sha256:9a31c18ba4881a116e448c52f3f5d3e14401cf7a9c43cc88f06f2a7f5428da0e \
|
||||
--hash=sha256:80796ea68e11624a0279d3b802f88a7fe7214122b97a15a6c97189934a2cc776 \
|
||||
--hash=sha256:f4019826a2dec066c909a1f483ef0dcf9325d6740cc0bd15308942b28b0930f7 \
|
||||
--hash=sha256:7248506981eeba23888b4140a69a53c4c0c0a386abcdca61ed8dd790a73e64b9 \
|
||||
--hash=sha256:a8955265d146e86fe2ce116394be4eaf0cb40314a79b19f11c4fa574cd639572 \
|
||||
--hash=sha256:c49187260043bd4c1d6a52186f9774f17d9b1da0a406798ebf4bfc12da166ade \
|
||||
--hash=sha256:c1d8b3d8dcb5c23ac1a8bf56422036f3f305a3c5a8bc8c354256579a1e2aa2c1 \
|
||||
--hash=sha256:9e389615bcecb8c782a87939d752340bb0a3a097e90bae54d7f0915bc12f45bd \
|
||||
--hash=sha256:d09ff358f75a874f69fa7d1c2b4acecf4282a950293fcfcf89aa606da8a9a500 \
|
||||
--hash=sha256:b69b4557aae7de18b7c174a917fe19873529d927ac592762d9771661875bbd40 \
|
||||
--hash=sha256:5de52b081a2775e76b971de9d997d85c4457fc0a09079e12d66849548ae60981 \
|
||||
--hash=sha256:e7d88fecb7b6250a1fd432e6dc64890342c372fce13dbfe4bb6f16348ad00c14 \
|
||||
--hash=sha256:1426e67e855ef7f5030c9184f4f1a9f4bfa020c31c962cd41fd129ec5aef4a6a \
|
||||
--hash=sha256:267dd2c66a5760c5f4d47e2ebcf8eeac7ef01e1ae6ae7a6d0d241a290068bc38 \
|
||||
--hash=sha256:e553eb489511cacf19eda6e52bc9e151316f0d721724997dda2c4d3079b778db \
|
||||
--hash=sha256:98b89b2c57f97ce2db7aeba60db173c84871d73b40e41a11ea95de1500ddc57e \
|
||||
--hash=sha256:e2b7e090188833bc58b2ae03fb864c22688654ebd2096bcf38bc860c4f38a3d8 \
|
||||
--hash=sha256:afa7d8b8d38ad40db8713ee053d41b36d87d6ae5ec5ad36f9210b548a18dc214 \
|
||||
--hash=sha256:4fc9c2ff7924b3a1fa326e1799e5dd58cac585d7fb25fe53ccaa1333b0453d65 \
|
||||
--hash=sha256:937db39a1ec5af3003b16357b2042bba67c88d43bc11aaa203fa8a5924524209 \
|
||||
--hash=sha256:ab22285797631df3b513b2cd3ecdc51cd8e3d36788e3991d93d0759d6883b027 \
|
||||
--hash=sha256:96e599b924ef009aa867f725b3249ee51d76489f484d3a45b4bd219c5ec6ed59 \
|
||||
--hash=sha256:bea842a0512be6a8007e585790bccd5d530520fc025ce63b03e139be373b0063 \
|
||||
--hash=sha256:e7175287f7fe7b1cc203bb958b17db40abd732690c1e18e700f10e0843a58598 \
|
||||
--hash=sha256:285ab352552f52f1398c912556d4d36d4ea9b8450e5c65d03809bf9886755533 \
|
||||
--hash=sha256:5576644b859197da7bbd8f8c7c2fb5dcc6cd505cadb42992d5f104c013f8a214 \
|
||||
--hash=sha256:b3b02911eb1f6ada203b0763ba924234629b51586f72a21faacc638269f4ced5
|
||||
cffi==1.11.5 \
|
||||
--hash=sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50 \
|
||||
--hash=sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596 \
|
||||
--hash=sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef \
|
||||
--hash=sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743 \
|
||||
--hash=sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f \
|
||||
--hash=sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31 \
|
||||
--hash=sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04 \
|
||||
--hash=sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6 \
|
||||
--hash=sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3 \
|
||||
--hash=sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6 \
|
||||
--hash=sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b \
|
||||
--hash=sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca \
|
||||
--hash=sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e \
|
||||
--hash=sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb \
|
||||
--hash=sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd \
|
||||
--hash=sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1 \
|
||||
--hash=sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917 \
|
||||
--hash=sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359 \
|
||||
--hash=sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f \
|
||||
--hash=sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95 \
|
||||
--hash=sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801 \
|
||||
--hash=sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257 \
|
||||
--hash=sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184 \
|
||||
--hash=sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc \
|
||||
--hash=sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085 \
|
||||
--hash=sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93 \
|
||||
--hash=sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2 \
|
||||
--hash=sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30 \
|
||||
--hash=sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5 \
|
||||
--hash=sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e \
|
||||
--hash=sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b \
|
||||
--hash=sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4
|
||||
ConfigArgParse==0.12.0 \
|
||||
--hash=sha256:28cd7d67669651f2a4518367838c49539457504584a139709b2b8f6c208ef339 \
|
||||
--no-binary ConfigArgParse
|
||||
|
|
@ -150,9 +146,9 @@ pytz==2015.7 \
|
|||
--hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \
|
||||
--hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \
|
||||
--hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3
|
||||
requests==2.12.1 \
|
||||
--hash=sha256:3f3f27a9d0f9092935efc78054ef324eb9f8166718270aefe036dfa1e4f68e1e \
|
||||
--hash=sha256:2109ecea94df90980be040490ff1d879971b024861539abb00054062388b612e
|
||||
requests==2.20.0 \
|
||||
--hash=sha256:99dcfdaaeb17caf6e526f32b6a7b780461512ab3f1d992187801694cba42770c \
|
||||
--hash=sha256:a84b8c9ab6239b578f22d1c21d51b696dcfe004032bb80ea832398d6909d7279
|
||||
six==1.10.0 \
|
||||
--hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
|
||||
--hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
|
||||
|
|
@ -189,3 +185,12 @@ zope.interface==4.1.3 \
|
|||
requests-toolbelt==0.8.0 \
|
||||
--hash=sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237 \
|
||||
--hash=sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5
|
||||
chardet==3.0.2 \
|
||||
--hash=sha256:4f7832e7c583348a9eddd927ee8514b3bf717c061f57b21dbe7697211454d9bb \
|
||||
--hash=sha256:6ebf56457934fdce01fb5ada5582762a84eed94cad43ed877964aebbdd8174c0
|
||||
urllib3==1.24.1 \
|
||||
--hash=sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39 \
|
||||
--hash=sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22
|
||||
certifi==2017.4.17 \
|
||||
--hash=sha256:f4318671072f030a33c7ca6acaef720ddd50ff124d1388e50c1bda4cbd6d7010 \
|
||||
--hash=sha256:f7527ebf7461582ce95f7a9e03dd141ce810d40590834f4ec20cddd54234c10a
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
#!/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
|
||||
|
|
@ -45,7 +43,7 @@ except ImportError:
|
|||
cmd = popenargs[0]
|
||||
raise CalledProcessError(retcode, cmd)
|
||||
return output
|
||||
from sys import exit, version_info, executable
|
||||
from sys import exit, version_info
|
||||
from tempfile import mkdtemp
|
||||
try:
|
||||
from urllib2 import build_opener, HTTPHandler, HTTPSHandler
|
||||
|
|
@ -57,7 +55,7 @@ except ImportError:
|
|||
from urllib.parse import urlparse # 3.4
|
||||
|
||||
|
||||
__version__ = 2, 0, 0
|
||||
__version__ = 1, 5, 1
|
||||
PIP_VERSION = '9.0.1'
|
||||
DEFAULT_INDEX_BASE = 'https://pypi.python.org'
|
||||
|
||||
|
|
@ -133,10 +131,8 @@ def hashed_download(url, temp, digest):
|
|||
|
||||
def get_index_base():
|
||||
"""Return the URL to the dir containing the "packages" folder.
|
||||
|
||||
Try to wring something out of PIP_INDEX_URL, if set. Hack "/simple" off the
|
||||
end if it's there; that is likely to give us the right dir.
|
||||
|
||||
"""
|
||||
env_var = environ.get('PIP_INDEX_URL', '').rstrip('/')
|
||||
if env_var:
|
||||
|
|
@ -150,7 +146,7 @@ def get_index_base():
|
|||
|
||||
|
||||
def main():
|
||||
pip_version = StrictVersion(check_output([executable, '-m', 'pip', '--version'])
|
||||
pip_version = StrictVersion(check_output(['pip', '--version'])
|
||||
.decode('utf-8').split()[1])
|
||||
min_pip_version = StrictVersion(PIP_VERSION)
|
||||
if pip_version >= min_pip_version:
|
||||
|
|
@ -163,7 +159,7 @@ def main():
|
|||
temp,
|
||||
digest)
|
||||
for path, digest in PACKAGES]
|
||||
check_output('{0} -m pip install --no-index --no-deps -U '.format(quote(executable)) +
|
||||
check_output('pip install --no-index --no-deps -U ' +
|
||||
# Disable cache since we're not using it and it otherwise
|
||||
# sometimes throws permission warnings:
|
||||
('--no-cache-dir ' if has_pip_cache else '') +
|
||||
|
|
@ -181,4 +177,4 @@ def main():
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
exit(main())
|
||||
|
|
|
|||
|
|
@ -27,6 +27,19 @@ def tests_dir():
|
|||
return dirname(abspath(__file__))
|
||||
|
||||
|
||||
def copy_stable(src, dst):
|
||||
"""
|
||||
Copy letsencrypt-auto, and replace its current version to its equivalent stable one.
|
||||
This is needed to test correctly the self-upgrade functionality.
|
||||
"""
|
||||
copy(src, dst)
|
||||
with open(dst, 'r') as file:
|
||||
filedata = file.read()
|
||||
filedata = re.sub(r'LE_AUTO_VERSION="(.*)\.dev0"', r'LE_AUTO_VERSION="\1"', filedata)
|
||||
with open(dst, 'w') as file:
|
||||
file.write(filedata)
|
||||
|
||||
|
||||
sys.path.insert(0, dirname(tests_dir()))
|
||||
from build import build as build_le_auto
|
||||
|
||||
|
|
@ -343,7 +356,7 @@ class AutoTests(TestCase):
|
|||
'v99.9.9/letsencrypt-auto': build_le_auto(version='99.9.9'),
|
||||
'v99.9.9/letsencrypt-auto.sig': signed('something else')}
|
||||
with serving(resources) as base_url:
|
||||
copy(LE_AUTO_PATH, le_auto_path)
|
||||
copy_stable(LE_AUTO_PATH, le_auto_path)
|
||||
try:
|
||||
out, err = run_le_auto(le_auto_path, venv_dir, base_url)
|
||||
except CalledProcessError as exc:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
acme[dev]==0.26.0
|
||||
acme[dev]==0.29.0
|
||||
|
|
|
|||
|
|
@ -1 +1,3 @@
|
|||
Be sure to edit the `master` section of `CHANGELOG.md` with a line describing this PR before it gets merged.
|
||||
Be sure to edit the `master` section of `CHANGELOG.md`. This includes a
|
||||
description of the change and ensuring the modified package(s) are listed as
|
||||
having been changed.
|
||||
|
|
|
|||
12
pytest.ini
Normal file
12
pytest.ini
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# This file isn't used while testing packages in tools/_release.sh so any
|
||||
# settings we want to also change there must be added to the release script
|
||||
# directly.
|
||||
[pytest]
|
||||
addopts = --numprocesses auto --pyargs
|
||||
# ResourceWarnings are ignored as errors, since they're raised at close
|
||||
# decodestring: https://github.com/rthalley/dnspython/issues/338
|
||||
# ignore our own TLS-SNI-01 warning
|
||||
filterwarnings =
|
||||
error
|
||||
ignore:decodestring:DeprecationWarning
|
||||
ignore:TLS-SNI-01:DeprecationWarning
|
||||
9
setup.py
9
setup.py
|
|
@ -31,13 +31,13 @@ version = meta['version']
|
|||
# specified here to avoid masking the more specific request requirements in
|
||||
# acme. See https://github.com/pypa/pip/issues/988 for more info.
|
||||
install_requires = [
|
||||
'acme>=0.26.0',
|
||||
'acme>=0.29.0',
|
||||
# We technically need ConfigArgParse 0.10.0 for Python 2.6 support, but
|
||||
# saying so here causes a runtime error against our temporary fork of 0.9.3
|
||||
# in which we added 2.6 support (see #2243), so we relax the requirement.
|
||||
'ConfigArgParse>=0.9.3',
|
||||
'configobj',
|
||||
'cryptography>=1.2', # load_pem_x509_certificate
|
||||
'cryptography>=1.2.3', # load_pem_x509_certificate
|
||||
'josepy',
|
||||
'mock',
|
||||
'parsedatetime>=1.3', # Calendar.parseDT
|
||||
|
|
@ -68,9 +68,10 @@ dev3_extras = [
|
|||
]
|
||||
|
||||
docs_extras = [
|
||||
# If you have Sphinx<1.5.1, you need docutils<0.13.1
|
||||
# https://github.com/sphinx-doc/sphinx/issues/3212
|
||||
'repoze.sphinx.autointerface',
|
||||
# sphinx.ext.imgconverter
|
||||
'Sphinx >=1.6',
|
||||
'Sphinx>=1.2', # Annotation support
|
||||
'sphinx_rtd_theme',
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ if [ ! -d ${BOULDERPATH} ]; then
|
|||
fi
|
||||
|
||||
cd ${BOULDERPATH}
|
||||
sed -i "s/FAKE_DNS: .*/FAKE_DNS: 10.77.77.1/" docker-compose.yml
|
||||
|
||||
docker-compose up -d boulder
|
||||
|
||||
|
|
@ -28,3 +27,6 @@ if ! curl http://localhost:4000/directory 2>/dev/null; then
|
|||
echo "timed out waiting for boulder to start"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Setup the DNS resolution used by boulder instance to docker host
|
||||
curl -X POST -d '{"ip":"10.77.77.1"}' http://localhost:8055/set-default-ipv4
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ CheckRenewHook() {
|
|||
TotalAndDistinctLines() {
|
||||
total=$1
|
||||
distinct=$2
|
||||
awk '{a[$1] = 1}; END {exit(NR !='$total' || length(a) !='$distinct')}'
|
||||
awk '{a[$1] = 1}; END {n = 0; for (i in a) { n++ }; exit(NR !='$total' || n !='$distinct')}'
|
||||
}
|
||||
|
||||
# Cleanup coverage data
|
||||
|
|
@ -207,10 +207,16 @@ common unregister
|
|||
|
||||
common register --email ex1@domain.org,ex2@domain.org
|
||||
|
||||
# TODO: When `certbot register --update-registration` is fully deprecated, delete the two following deprecated uses
|
||||
|
||||
common register --update-registration --email ex1@domain.org
|
||||
|
||||
common register --update-registration --email ex1@domain.org,ex2@domain.org
|
||||
|
||||
common update_account --email example@domain.org
|
||||
|
||||
common update_account --email ex1@domain.org,ex2@domain.org
|
||||
|
||||
common plugins --init --prepare | grep webroot
|
||||
|
||||
# We start a server listening on the port for the
|
||||
|
|
@ -280,7 +286,38 @@ CheckCertCount() {
|
|||
fi
|
||||
}
|
||||
|
||||
CheckPermissions() {
|
||||
# Args: <filepath_1> <filepath_2> <mask>
|
||||
# Checks mode of two files match under <mask>
|
||||
masked_mode() { echo $((0`stat -c %a $1` & 0$2)); }
|
||||
if [ `masked_mode $1 $3` -ne `masked_mode $2 $3` ] ; then
|
||||
echo "With $3 mask, expected mode `masked_mode $1 $3`, got `masked_mode $2 $3` on file $2"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
CheckGID() {
|
||||
# Args: <filepath_1> <filepath_2>
|
||||
# Checks group owner of two files match
|
||||
group_owner() { echo `stat -c %G $1`; }
|
||||
if [ `group_owner $1` != `group_owner $2` ] ; then
|
||||
echo "Expected group owner `group_owner $1`, got `group_owner $2` on file $2"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
CheckOthersPermission() {
|
||||
# Args: <filepath_1> <expected mode>
|
||||
# Tests file's other/world permission against expected mode
|
||||
other_permission=$((0`stat -c %a $1` & 07))
|
||||
if [ $other_permission -ne $2 ] ; then
|
||||
echo "Expected file $1 to have others mode $2, got $other_permission instead"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
CheckCertCount "le.wtf" 1
|
||||
|
||||
# This won't renew (because it's not time yet)
|
||||
common_no_force_renew renew
|
||||
CheckCertCount "le.wtf" 1
|
||||
|
|
@ -294,6 +331,12 @@ rm -rf "$renewal_hooks_root"
|
|||
common renew --cert-name le.wtf --authenticator manual
|
||||
CheckCertCount "le.wtf" 2
|
||||
|
||||
CheckOthersPermission "${root}/conf/archive/le.wtf/privkey1.pem" 0
|
||||
CheckOthersPermission "${root}/conf/archive/le.wtf/privkey2.pem" 0
|
||||
CheckPermissions "${root}/conf/archive/le.wtf/privkey1.pem" "${root}/conf/archive/le.wtf/privkey2.pem" 074
|
||||
CheckGID "${root}/conf/archive/le.wtf/privkey1.pem" "${root}/conf/archive/le.wtf/privkey2.pem"
|
||||
chmod 0444 "${root}/conf/archive/le.wtf/privkey2.pem"
|
||||
|
||||
# test renewal with no executables in hook directories
|
||||
for hook_dir in $renewal_hooks_dirs; do
|
||||
touch "$hook_dir/file"
|
||||
|
|
@ -310,6 +353,10 @@ CreateDirHooks
|
|||
sed -i "4arenew_before_expiry = 4 years" "$root/conf/renewal/le.wtf.conf"
|
||||
common_no_force_renew renew --rsa-key-size 2048 --no-directory-hooks
|
||||
CheckCertCount "le.wtf" 3
|
||||
CheckGID "${root}/conf/archive/le.wtf/privkey2.pem" "${root}/conf/archive/le.wtf/privkey3.pem"
|
||||
CheckPermissions "${root}/conf/archive/le.wtf/privkey2.pem" "${root}/conf/archive/le.wtf/privkey3.pem" 074
|
||||
CheckOthersPermission "${root}/conf/archive/le.wtf/privkey3.pem" 04
|
||||
|
||||
if [ -s "$HOOK_DIRS_TEST" ]; then
|
||||
echo "Directory hooks were executed with --no-directory-hooks!" >&2
|
||||
exit 1
|
||||
|
|
|
|||
16
tests/certbot-pebble-integration.sh
Executable file
16
tests/certbot-pebble-integration.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
# Simple integration test. Make sure to activate virtualenv beforehand
|
||||
# (source venv/bin/activate) and that you are running Pebble test
|
||||
# instance (see ./pebble-fetch.sh).
|
||||
|
||||
cleanup_and_exit() {
|
||||
EXIT_STATUS=$?
|
||||
unset SERVER
|
||||
exit $EXIT_STATUS
|
||||
}
|
||||
|
||||
trap cleanup_and_exit EXIT
|
||||
|
||||
export SERVER=https://localhost:14000/dir
|
||||
|
||||
./tests/certbot-boulder-integration.sh
|
||||
|
|
@ -3,12 +3,15 @@
|
|||
root=${root:-$(mktemp -d -t leitXXXX)}
|
||||
echo "Root integration tests directory: $root"
|
||||
config_dir="$root/conf"
|
||||
store_flags="--config-dir $config_dir --work-dir $root/work"
|
||||
store_flags="$store_flags --logs-dir $root/logs"
|
||||
tls_sni_01_port=5001
|
||||
http_01_port=5002
|
||||
sources="acme/,$(ls -dm certbot*/ | tr -d ' \n')"
|
||||
export root config_dir store_flags tls_sni_01_port http_01_port sources
|
||||
export root config_dir tls_sni_01_port http_01_port sources
|
||||
certbot_path="$(command -v certbot)"
|
||||
# Flags that are added here will be added to Certbot calls within
|
||||
# certbot_test_no_force_renew.
|
||||
other_flags="--config-dir $config_dir --work-dir $root/work"
|
||||
other_flags="$other_flags --logs-dir $root/logs"
|
||||
|
||||
certbot_test () {
|
||||
certbot_test_no_force_renew \
|
||||
|
|
@ -16,11 +19,35 @@ certbot_test () {
|
|||
"$@"
|
||||
}
|
||||
|
||||
# Succeeds if Certbot version is at least the given version number and fails
|
||||
# otherwise. This is useful for making sure Certbot has certain features
|
||||
# available. The patch version is currently ignored.
|
||||
#
|
||||
# Arguments:
|
||||
# First argument is the minimum major version
|
||||
# Second argument is the minimum minor version
|
||||
version_at_least () {
|
||||
# Certbot major and minor version (e.g. 0.30)
|
||||
major_minor=$("$certbot_path" --version 2>&1 | cut -d' ' -f2 | cut -d. -f1,2)
|
||||
major=$(echo "$major_minor" | cut -d. -f1)
|
||||
minor=$(echo "$major_minor" | cut -d. -f2)
|
||||
# Test that either the major version is greater or major version is equal
|
||||
# and minor version is greater than or equal to.
|
||||
[ \( "$major" -gt "$1" \) -o \( "$major" -eq "$1" -a "$minor" -ge "$2" \) ]
|
||||
}
|
||||
|
||||
# Use local ACMEv2 endpoint if requested and SERVER isn't already set.
|
||||
if [ "${BOULDER_INTEGRATION:-v1}" = "v2" -a -z "${SERVER:+x}" ]; then
|
||||
SERVER="http://localhost:4001/directory"
|
||||
fi
|
||||
|
||||
# --no-random-sleep-on-renew was added in
|
||||
# https://github.com/certbot/certbot/pull/6599 and first released in Certbot
|
||||
# 0.30.0.
|
||||
if version_at_least 0 30; then
|
||||
other_flags="$other_flags --no-random-sleep-on-renew"
|
||||
fi
|
||||
|
||||
certbot_test_no_force_renew () {
|
||||
omit_patterns="*/*.egg-info/*,*/dns_common*,*/setup.py,*/test_*,*/tests/*"
|
||||
omit_patterns="$omit_patterns,*_test.py,*_test_*,certbot-apache/*"
|
||||
|
|
@ -30,13 +57,13 @@ certbot_test_no_force_renew () {
|
|||
--append \
|
||||
--source $sources \
|
||||
--omit $omit_patterns \
|
||||
$(command -v certbot) \
|
||||
"$certbot_path" \
|
||||
--server "${SERVER:-http://localhost:4000/directory}" \
|
||||
--no-verify-ssl \
|
||||
--tls-sni-01-port $tls_sni_01_port \
|
||||
--http-01-port $http_01_port \
|
||||
--manual-public-ip-logging-ok \
|
||||
$store_flags \
|
||||
$other_flags \
|
||||
--non-interactive \
|
||||
--no-redirect \
|
||||
--agree-tos \
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ if [ $? -ne 0 ] ; then
|
|||
fi
|
||||
|
||||
if [ "$OS_TYPE" = "ubuntu" ] ; then
|
||||
export SERVER="$BOULDER_URL"
|
||||
venv/bin/tox -e apacheconftest
|
||||
else
|
||||
echo Not running hackish apache tests on $OS_TYPE
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ VERSION=$(letsencrypt-auto-source/version.py)
|
|||
export VENV_ARGS="-p $PYTHON"
|
||||
|
||||
# setup venv
|
||||
tools/_venv_common.sh --requirement letsencrypt-auto-source/pieces/dependency-requirements.txt
|
||||
tools/_venv_common.py --requirement letsencrypt-auto-source/pieces/dependency-requirements.txt
|
||||
. ./venv/bin/activate
|
||||
|
||||
# build sdists
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ def validate_scripts_content(repo_path, temp_cwd):
|
|||
# Compare file against the latest released version
|
||||
latest_version = subprocess.check_output(
|
||||
[sys.executable, 'fetch.py', '--latest-version'], cwd=temp_cwd)
|
||||
subprocess.call(
|
||||
subprocess.check_call(
|
||||
[sys.executable, 'fetch.py', '--le-auto-script',
|
||||
'v{0}'.format(latest_version.decode().strip())], cwd=temp_cwd)
|
||||
if compare_files(
|
||||
|
|
@ -95,7 +95,7 @@ def main():
|
|||
os.path.normpath(os.path.join(repo_path, 'letsencrypt-auto-source/letsencrypt-auto')),
|
||||
os.path.join(temp_cwd, 'original-lea')
|
||||
)
|
||||
subprocess.call([sys.executable, os.path.normpath(os.path.join(
|
||||
subprocess.check_call([sys.executable, os.path.normpath(os.path.join(
|
||||
repo_path, 'letsencrypt-auto-source/build.py'))])
|
||||
shutil.copyfile(
|
||||
os.path.normpath(os.path.join(repo_path, 'letsencrypt-auto-source/letsencrypt-auto')),
|
||||
|
|
|
|||
41
tests/pebble-fetch.sh
Executable file
41
tests/pebble-fetch.sh
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
# Download and run Pebble instance for integration testing
|
||||
set -xe
|
||||
|
||||
PEBBLE_VERSION=2018-11-02
|
||||
|
||||
# We reuse the same GOPATH-style directory than for Boulder.
|
||||
# Pebble does not need it, but it will make the installation consistent with Boulder's one.
|
||||
export GOPATH=${GOPATH:-$HOME/gopath}
|
||||
PEBBLEPATH=${PEBBLEPATH:-$GOPATH/src/github.com/letsencrypt/pebble}
|
||||
|
||||
mkdir -p ${PEBBLEPATH}
|
||||
|
||||
cat << UNLIKELY_EOF > "$PEBBLEPATH/docker-compose.yml"
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
pebble:
|
||||
image: letsencrypt/pebble:${PEBBLE_VERSION}
|
||||
command: pebble -strict ${PEBBLE_STRICT:-false} -dnsserver 10.77.77.1
|
||||
ports:
|
||||
- 14000:14000
|
||||
environment:
|
||||
- PEBBLE_VA_NOSLEEP=1
|
||||
UNLIKELY_EOF
|
||||
|
||||
docker-compose -f "$PEBBLEPATH/docker-compose.yml" up -d pebble
|
||||
|
||||
set +x # reduce verbosity while waiting for boulder
|
||||
for n in `seq 1 150` ; do
|
||||
if curl -k https://localhost:14000/dir 2>/dev/null; then
|
||||
break
|
||||
else
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! curl -k https://localhost:14000/dir 2>/dev/null; then
|
||||
echo "timed out waiting for pebble to start"
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/bash -e
|
||||
#
|
||||
# Set up the test environment for macOS on Travis.
|
||||
|
||||
# Install the given package with brew if it's not already installed.
|
||||
brew_install() {
|
||||
if ! brew list "$1" > /dev/null 2>&1; then
|
||||
brew install "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
brew_install augeas
|
||||
brew_install python
|
||||
brew_install python3
|
||||
|
||||
# Ensure we use python from brew.
|
||||
brew link python
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue