mirror of
https://github.com/certbot/certbot.git
synced 2026-06-04 06:15:36 -04:00
Merge branch 'master' into resolve-merge-conflicts-in-test-everything
This commit is contained in:
commit
b995c8318d
71 changed files with 811 additions and 614 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -6,7 +6,8 @@ dist*/
|
|||
/venv*/
|
||||
/kgs/
|
||||
/.tox/
|
||||
/releases/
|
||||
/releases*/
|
||||
/log*
|
||||
letsencrypt.log
|
||||
certbot.log
|
||||
letsencrypt-auto-source/letsencrypt-auto.sig.lzma.base64
|
||||
|
|
@ -39,6 +40,7 @@ tests/letstest/venv/
|
|||
# pytest cache
|
||||
.cache
|
||||
.mypy_cache/
|
||||
.pytest_cache/
|
||||
|
||||
# docker files
|
||||
.docker
|
||||
|
|
|
|||
76
CHANGELOG.md
76
CHANGELOG.md
|
|
@ -2,6 +2,82 @@
|
|||
|
||||
Certbot adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 0.28.0 - master
|
||||
|
||||
### Added
|
||||
|
||||
* `revoke` accepts `--cert-name`, and doesn't accept both `--cert-name` and `--cert-path`.
|
||||
|
||||
### Changed
|
||||
|
||||
*
|
||||
|
||||
### Fixed
|
||||
|
||||
* Match Nginx parser update in allowing variable names to start with `${`.
|
||||
* Correct OVH integration tests on machines without internet access.
|
||||
|
||||
## 0.27.1 - 2018-09-06
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed parameter name in OpenSUSE overrides for default parameters in the
|
||||
Apache plugin. Certbot on OpenSUSE works again.
|
||||
|
||||
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-apache
|
||||
|
||||
More details about these changes can be found on our GitHub repo:
|
||||
https://github.com/certbot/certbot/milestone/60?closed=1
|
||||
|
||||
## 0.27.0 - 2018-09-05
|
||||
|
||||
### Added
|
||||
|
||||
* The Apache plugin now accepts the parameter --apache-ctl which can be
|
||||
used to configure the path to the Apache control script.
|
||||
|
||||
### Changed
|
||||
|
||||
* When using `acme.client.ClientV2` (or
|
||||
`acme.client.BackwardsCompatibleClientV2` with an ACME server that supports a
|
||||
newer version of the ACME protocol), an `acme.errors.ConflictError` will be
|
||||
raised if you try to create an ACME account with a key that has already been
|
||||
used. Previously, a JSON parsing error was raised in this scenario when using
|
||||
the library with Let's Encrypt's ACMEv2 endpoint.
|
||||
|
||||
### Fixed
|
||||
|
||||
* When Apache is not installed, Certbot's Apache plugin no longer prints
|
||||
messages about being unable to find apachectl to the terminal when the plugin
|
||||
is not selected.
|
||||
* If you're using the Apache plugin with the --apache-vhost-root flag set to a
|
||||
directory containing a disabled virtual host for the domain you're requesting
|
||||
a certificate for, the virtual host will now be temporarily enabled if
|
||||
necessary to pass the HTTP challenge.
|
||||
* The documentation for the Certbot package can now be built using Sphinx 1.6+.
|
||||
* You can now call `query_registration` without having to first call
|
||||
`new_account` on `acme.client.ClientV2` objects.
|
||||
* The requirement of `setuptools>=1.0` has been removed from `certbot-dns-ovh`.
|
||||
* Names in certbot-dns-sakuracloud's tests have been updated to refer to Sakura
|
||||
Cloud rather than NS1 whose plugin certbot-dns-sakuracloud was based on.
|
||||
|
||||
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-ovh
|
||||
* certbot-dns-sakuracloud
|
||||
|
||||
More details about these changes can be found on our GitHub repo:
|
||||
https://github.com/certbot/certbot/milestone/57?closed=1
|
||||
|
||||
## 0.26.1 - 2018-07-17
|
||||
|
||||
### Fixed
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
ChangeLog
|
||||
=========
|
||||
|
||||
To see the changes in a given release, view the issues closed in a given
|
||||
release's GitHub milestone:
|
||||
|
||||
- `Past releases <https://github.com/certbot/certbot/milestones?state=closed>`_
|
||||
- `Upcoming releases <https://github.com/certbot/certbot/milestones>`_
|
||||
|
|
@ -5,7 +5,7 @@ EXPOSE 80 443
|
|||
VOLUME /etc/letsencrypt /var/lib/letsencrypt
|
||||
WORKDIR /opt/certbot
|
||||
|
||||
COPY CHANGES.rst README.rst setup.py src/
|
||||
COPY CHANGELOG.md README.rst setup.py src/
|
||||
COPY acme src/acme
|
||||
COPY certbot src/certbot
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
|||
# Dockerfile we make sure we cache as much as possible
|
||||
|
||||
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/certbot/src/
|
||||
COPY setup.py README.rst CHANGELOG.md MANIFEST.in letsencrypt-auto-source/pieces/pipstrap.py /opt/certbot/src/
|
||||
|
||||
# all above files are necessary for setup.py and venv setup, however,
|
||||
# package source code directory has to be copied separately to a
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
include README.rst
|
||||
include CHANGES.rst
|
||||
include CHANGELOG.md
|
||||
include CONTRIBUTING.md
|
||||
include LICENSE.txt
|
||||
include linter_plugin.py
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from setuptools import find_packages
|
|||
from setuptools.command.test import test as TestCommand
|
||||
import sys
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
12
appveyor.yml
Normal file
12
appveyor.yml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# AppVeyor CI pipeline, executed on Windows Server 2016/2012 R2
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /^\d+\.\d+\.x$/ # version branches like X.X.X
|
||||
- /^test-.*$/
|
||||
|
||||
build: off
|
||||
|
||||
test_script:
|
||||
- ps: Write-Host "Hello, world!"
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ class OpenSUSEConfigurator(configurator.ApacheConfigurator):
|
|||
enmod="a2enmod",
|
||||
dismod="a2dismod",
|
||||
le_vhost_ext="-le-ssl.conf",
|
||||
handle_mods=False,
|
||||
handle_modules=False,
|
||||
handle_sites=False,
|
||||
challenge_location="/etc/apache2/vhosts.d",
|
||||
MOD_SSL_CONF_SRC=pkg_resources.resource_filename(
|
||||
|
|
|
|||
|
|
@ -115,6 +115,18 @@ class MultipleVhostsTest(util.ApacheTest):
|
|||
# Weak test..
|
||||
ApacheConfigurator.add_parser_arguments(mock.MagicMock())
|
||||
|
||||
def test_add_parser_arguments_all_configurators(self): # pylint: disable=no-self-use
|
||||
from certbot_apache.entrypoint import OVERRIDE_CLASSES
|
||||
for cls in OVERRIDE_CLASSES.values():
|
||||
cls.add_parser_arguments(mock.MagicMock())
|
||||
|
||||
def test_all_configurators_defaults_defined(self):
|
||||
from certbot_apache.entrypoint import OVERRIDE_CLASSES
|
||||
from certbot_apache.configurator import ApacheConfigurator
|
||||
parameters = set(ApacheConfigurator.OS_DEFAULTS.keys())
|
||||
for cls in OVERRIDE_CLASSES.values():
|
||||
self.assertTrue(parameters.issubset(set(cls.OS_DEFAULTS.keys())))
|
||||
|
||||
def test_constant(self):
|
||||
self.assertTrue("debian_apache_2_4/multiple_vhosts/apache" in
|
||||
self.config.option("server_root"))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
26
certbot-auto
26
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.26.1"
|
||||
LE_AUTO_VERSION="0.27.1"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
|
|
@ -1197,18 +1197,18 @@ letsencrypt==0.7.0 \
|
|||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.26.1 \
|
||||
--hash=sha256:4e2ffdeebb7f5097600bcb1ca19131441fa021f952b443ca7454a279337af609 \
|
||||
--hash=sha256:4983513d63f7f36e24a07873ca2d6ea1c0101aa6cb1cd825cda02ed520f6ca66
|
||||
acme==0.26.1 \
|
||||
--hash=sha256:d47841e66adc1336ecca2f0d41a247c1b62307c981be6d07996bbf3f95af1dc5 \
|
||||
--hash=sha256:86e7b5f4654cb19215f16c0e6225750db7421f68ef6a0a040a61796f24e690be
|
||||
certbot-apache==0.26.1 \
|
||||
--hash=sha256:c16acb49bd4f84fff25bcbb7eaf74412145efe9b68ce46e1803be538894f2ce3 \
|
||||
--hash=sha256:b7fa327e987b892d64163e7519bdeaf9723d78275ef6c438272848894ace6d87
|
||||
certbot-nginx==0.26.1 \
|
||||
--hash=sha256:c0048dc83672dc90805a8ddf513be3e48c841d6e91607e91e8657c1785d65660 \
|
||||
--hash=sha256:d0c95a32625e0f1612d7fcf9021e6e050ba3d879823489d1edd2478a78ae6624
|
||||
certbot==0.27.1 \
|
||||
--hash=sha256:89a8d8e44e272ee970259c93fa2ff2c9f063da8fd88a56d7ca30d7a2218791ea \
|
||||
--hash=sha256:3570bd14ed223c752f309dbd082044bd9f11a339d21671e70a2eeae4e51ed02a
|
||||
acme==0.27.1 \
|
||||
--hash=sha256:0d42cfc9050a2e1d6d4e6b66334df8173778db0b3fe7a2b3bcb58f7034913597 \
|
||||
--hash=sha256:31a7b9023ce183616e6ebd5d783e842c3d68696ff70db59a06db9feea8f54f90
|
||||
certbot-apache==0.27.1 \
|
||||
--hash=sha256:1c73297e6a59cebcf5f5692025d4013ccd02c858bdc946fee3c6613f62bb9414 \
|
||||
--hash=sha256:61d6d706d49d726b53a831a2ea9099bd6c02657ff537a166dd197cd5f494d854
|
||||
certbot-nginx==0.27.1 \
|
||||
--hash=sha256:9772198bcfde9b68e448c15c3801b3cf9d20eb9ea9da1d9f4f9a7692b0fc2314 \
|
||||
--hash=sha256:ff5b849a9b4e3d1fd50ea351a1393738382fc9bd47bc5ac18c343d11a691349f
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only
|
|||
# the above is not likely to change, so by putting it further up the
|
||||
# Dockerfile we make sure we cache as much as possible
|
||||
|
||||
COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini .pylintrc /opt/certbot/src/
|
||||
COPY setup.py README.rst CHANGELOG.md MANIFEST.in linter_plugin.py tox.cover.sh tox.ini .pylintrc /opt/certbot/src/
|
||||
|
||||
# all above files are necessary for setup.py, however, package source
|
||||
# code directory has to be copied separately to a subdirectory...
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import sys
|
|||
from setuptools import setup
|
||||
from setuptools import find_packages
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
install_requires = [
|
||||
'acme>=0.21.1',
|
||||
'certbot>=0.21.1',
|
||||
'dns-lexicon>=2.2.1', # Support for >1 TXT record per name
|
||||
'dns-lexicon>=2.7.3', # Correct OVH integration tests
|
||||
'mock',
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.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.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Please update tox.ini when modifying dependency version requirements
|
||||
install_requires = [
|
||||
|
|
|
|||
|
|
@ -136,7 +136,9 @@ class NginxConfigurator(common.Installer):
|
|||
"""
|
||||
# Verify Nginx is installed
|
||||
if not util.exe_exists(self.conf('ctl')):
|
||||
raise errors.NoInstallationError
|
||||
raise errors.NoInstallationError(
|
||||
"Could not find a usable 'nginx' binary. Ensure nginx exists, "
|
||||
"the binary is executable, and your PATH is set correctly.")
|
||||
|
||||
# Make sure configuration is valid
|
||||
self.config_test()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class RawNginxParser(object):
|
|||
dquoted = QuotedString('"', multiline=True, unquoteResults=False, escChar='\\')
|
||||
squoted = QuotedString("'", multiline=True, unquoteResults=False, escChar='\\')
|
||||
quoted = dquoted | squoted
|
||||
head_tokenchars = Regex(r"[^{};\s'\"]") # if (last_space)
|
||||
head_tokenchars = Regex(r"(\$\{)|[^{};\s'\"]") # if (last_space)
|
||||
tail_tokenchars = Regex(r"(\$\{)|[^{;\s]") # else
|
||||
tokenchars = Combine(head_tokenchars + ZeroOrMore(tail_tokenchars))
|
||||
paren_quote_extend = Combine(quoted + Literal(')') + ZeroOrMore(tail_tokenchars))
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ class NginxParser(object):
|
|||
return os.path.join(self.root, name)
|
||||
|
||||
raise errors.NoInstallationError(
|
||||
"Could not find configuration root")
|
||||
"Could not find Nginx root configuration file (nginx.conf)")
|
||||
|
||||
def filedump(self, ext='tmp', lazy=True):
|
||||
"""Dumps parsed configurations into files.
|
||||
|
|
@ -395,12 +395,17 @@ class NginxParser(object):
|
|||
addr.ipv6only = False
|
||||
for directive in enclosing_block[new_vhost.path[-1]][1]:
|
||||
if len(directive) > 0 and directive[0] == 'listen':
|
||||
if 'default_server' in directive:
|
||||
del directive[directive.index('default_server')]
|
||||
if 'default' in directive:
|
||||
del directive[directive.index('default')]
|
||||
if 'ipv6only=on' in directive:
|
||||
del directive[directive.index('ipv6only=on')]
|
||||
# Exclude one-time use parameters which will cause an error if repeated.
|
||||
# https://nginx.org/en/docs/http/ngx_http_core_module.html#listen
|
||||
exclude = set(('default_server', 'default', 'setfib', 'fastopen', 'backlog',
|
||||
'rcvbuf', 'sndbuf', 'accept_filter', 'deferred', 'bind',
|
||||
'ipv6only', 'reuseport', 'so_keepalive'))
|
||||
|
||||
for param in exclude:
|
||||
# See: github.com/certbot/certbot/pull/6223#pullrequestreview-143019225
|
||||
keys = [x.split('=')[0] for x in directive]
|
||||
if param in keys:
|
||||
del directive[keys.index(param)]
|
||||
return new_vhost
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -271,6 +271,8 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
location ~ ^/users/(.+\.(?:gif|jpe?g|png))$ {
|
||||
alias /data/w3/images/$1;
|
||||
}
|
||||
|
||||
proxy_set_header X-Origin-URI ${scheme}://${http_host}/$request_uri;
|
||||
"""
|
||||
parsed = loads(test)
|
||||
self.assertEqual(parsed, [[['if', '($http_user_agent', '~', 'MSIE)'],
|
||||
|
|
@ -281,7 +283,8 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
[['return', '403']]], [['if', '($args', '~', 'post=140)'],
|
||||
[['rewrite', '^', 'http://example.com/']]],
|
||||
[['location', '~', '^/users/(.+\\.(?:gif|jpe?g|png))$'],
|
||||
[['alias', '/data/w3/images/$1']]]]
|
||||
[['alias', '/data/w3/images/$1']]],
|
||||
['proxy_set_header', 'X-Origin-URI', '${scheme}://${http_host}/$request_uri']]
|
||||
)
|
||||
|
||||
def test_edge_cases(self):
|
||||
|
|
@ -289,10 +292,6 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
parsed = loads(r'"hello\""; # blah "heh heh"')
|
||||
self.assertEqual(parsed, [['"hello\\""'], ['#', ' blah "heh heh"']])
|
||||
|
||||
# empty var as block
|
||||
parsed = loads(r"${}")
|
||||
self.assertEqual(parsed, [[['$'], []]])
|
||||
|
||||
# if with comment
|
||||
parsed = loads("""if ($http_cookie ~* "id=([^;]+)(?:;|$)") { # blah )
|
||||
}""")
|
||||
|
|
@ -342,10 +341,9 @@ class TestRawNginxParser(unittest.TestCase):
|
|||
])
|
||||
|
||||
# variable weirdness
|
||||
parsed = loads("directive $var;")
|
||||
self.assertEqual(parsed, [['directive', '$var']])
|
||||
parsed = loads("directive $var ${var} $ ${};")
|
||||
self.assertEqual(parsed, [['directive', '$var', '${var}', '$', '${}']])
|
||||
self.assertRaises(ParseException, loads, "server {server_name test.com};")
|
||||
self.assertRaises(ParseException, loads, "directive ${var};")
|
||||
self.assertEqual(loads("blag${dfgdfg};"), [['blag${dfgdfg}']])
|
||||
self.assertRaises(ParseException, loads, "blag${dfgdf{g};")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
acme[dev]==0.26.0
|
||||
-e .[dev]
|
||||
certbot[dev]==0.22.0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from setuptools import setup
|
|||
from setuptools import find_packages
|
||||
|
||||
|
||||
version = '0.27.0.dev0'
|
||||
version = '0.28.0.dev0'
|
||||
|
||||
# Remember to update local-oldest-requirements.txt when changing the minimum
|
||||
# acme/certbot version.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
"""Certbot client."""
|
||||
|
||||
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
|
||||
__version__ = '0.27.0.dev0'
|
||||
__version__ = '0.28.0.dev0'
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import zope.component
|
|||
from acme import fields as acme_fields
|
||||
from acme import messages
|
||||
|
||||
from certbot import compat
|
||||
from certbot import constants
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
|
@ -140,7 +141,7 @@ class AccountFileStorage(interfaces.AccountStorage):
|
|||
"""
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
util.make_or_verify_dir(config.accounts_dir, 0o700, os.geteuid(),
|
||||
util.make_or_verify_dir(config.accounts_dir, 0o700, compat.os_geteuid(),
|
||||
self.config.strict_permissions)
|
||||
|
||||
def _account_dir_path(self, account_id):
|
||||
|
|
@ -323,7 +324,7 @@ class AccountFileStorage(interfaces.AccountStorage):
|
|||
|
||||
def _save(self, account, acme, regr_only):
|
||||
account_dir_path = self._account_dir_path(account.id)
|
||||
util.make_or_verify_dir(account_dir_path, 0o700, os.geteuid(),
|
||||
util.make_or_verify_dir(account_dir_path, 0o700, compat.os_geteuid(),
|
||||
self.config.strict_permissions)
|
||||
try:
|
||||
with open(self._regr_path(account_dir_path), "w") as regr_file:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import traceback
|
|||
import zope.component
|
||||
|
||||
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
|
||||
from certbot import compat
|
||||
from certbot import crypto_util
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
|
@ -104,7 +105,7 @@ def lineage_for_certname(cli_config, certname):
|
|||
"""Find a lineage object with name certname."""
|
||||
configs_dir = cli_config.renewal_configs_dir
|
||||
# Verify the directory is there
|
||||
util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
|
||||
util.make_or_verify_dir(configs_dir, mode=0o755, uid=compat.os_geteuid())
|
||||
try:
|
||||
renewal_file = storage.renewal_file_for_certname(cli_config, certname)
|
||||
except errors.CertStorageError:
|
||||
|
|
@ -374,7 +375,7 @@ def _search_lineages(cli_config, func, initial_rv, *args):
|
|||
"""
|
||||
configs_dir = cli_config.renewal_configs_dir
|
||||
# Verify the directory is there
|
||||
util.make_or_verify_dir(configs_dir, mode=0o755, uid=os.geteuid())
|
||||
util.make_or_verify_dir(configs_dir, mode=0o755, uid=compat.os_geteuid())
|
||||
|
||||
rv = initial_rv
|
||||
for renewal_file in storage.renewal_conf_files(cli_config):
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ obtain, install, and renew certificates:
|
|||
|
||||
manage certificates:
|
||||
certificates Display information about certificates you have from Certbot
|
||||
revoke Revoke a certificate (supply --cert-path)
|
||||
revoke Revoke a certificate (supply --cert-path or --cert-name)
|
||||
delete Delete a certificate
|
||||
|
||||
manage your account with Let's Encrypt:
|
||||
|
|
@ -387,9 +387,10 @@ VERB_HELP = [
|
|||
"usage": "\n\n certbot delete --cert-name CERTNAME\n\n"
|
||||
}),
|
||||
("revoke", {
|
||||
"short": "Revoke a certificate specified with --cert-path",
|
||||
"short": "Revoke a certificate specified with --cert-path or --cert-name",
|
||||
"opts": "Options for revocation of certificates",
|
||||
"usage": "\n\n certbot revoke --cert-path /path/to/fullchain.pem [options]\n\n"
|
||||
"usage": "\n\n certbot revoke [--cert-path /path/to/fullchain.pem | "
|
||||
"--cert-name example.com] [options]\n\n"
|
||||
}),
|
||||
("register", {
|
||||
"short": "Register for account with Let's Encrypt / other ACME server",
|
||||
|
|
@ -1333,7 +1334,7 @@ def _paths_parser(helpful):
|
|||
add(sections, "--cert-path", type=os.path.abspath,
|
||||
default=flag_default("auth_cert_path"), help=cph)
|
||||
elif verb == "revoke":
|
||||
add(sections, "--cert-path", type=read_file, required=True, help=cph)
|
||||
add(sections, "--cert-path", type=read_file, required=False, help=cph)
|
||||
else:
|
||||
add(sections, "--cert-path", type=os.path.abspath, help=cph)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import certbot
|
|||
from certbot import account
|
||||
from certbot import auth_handler
|
||||
from certbot import cli
|
||||
from certbot import compat
|
||||
from certbot import constants
|
||||
from certbot import crypto_util
|
||||
from certbot import eff
|
||||
|
|
@ -447,7 +448,7 @@ class Client(object):
|
|||
"""
|
||||
for path in cert_path, chain_path, fullchain_path:
|
||||
util.make_or_verify_dir(
|
||||
os.path.dirname(path), 0o755, os.geteuid(),
|
||||
os.path.dirname(path), 0o755, compat.os_geteuid(),
|
||||
self.config.strict_permissions)
|
||||
|
||||
|
||||
|
|
|
|||
140
certbot/compat.py
Normal file
140
certbot/compat.py
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
"""
|
||||
Compatibility layer to run certbot both on Linux and Windows.
|
||||
|
||||
The approach used here is similar to Modernizr for Web browsers.
|
||||
We do not check the plateform type to determine if a particular logic is supported.
|
||||
Instead, we apply a logic, and then fallback to another logic if first logic
|
||||
is not supported at runtime.
|
||||
|
||||
Then logic chains are abstracted into single functions to be exposed to certbot.
|
||||
"""
|
||||
import os
|
||||
import select
|
||||
import sys
|
||||
import errno
|
||||
import ctypes
|
||||
|
||||
from certbot import errors
|
||||
|
||||
try:
|
||||
# Linux specific
|
||||
import fcntl # pylint: disable=import-error
|
||||
except ImportError:
|
||||
# Windows specific
|
||||
import msvcrt # pylint: disable=import-error
|
||||
|
||||
UNPRIVILEGED_SUBCOMMANDS_ALLOWED = [
|
||||
'certificates', 'enhance', 'revoke', 'delete',
|
||||
'register', 'unregister', 'config_changes', 'plugins']
|
||||
def raise_for_non_administrative_windows_rights(subcommand):
|
||||
"""
|
||||
On Windows, raise if current shell does not have the administrative rights.
|
||||
Do nothing on Linux.
|
||||
|
||||
:param str subcommand: The subcommand (like 'certonly') passed to the certbot client.
|
||||
|
||||
:raises .errors.Error: If the provided subcommand must be run on a shell with
|
||||
administrative rights, and current shell does not have these rights.
|
||||
|
||||
"""
|
||||
# Why not simply try ctypes.windll.shell32.IsUserAnAdmin() and catch AttributeError ?
|
||||
# Because windll exists only on a Windows runtime, and static code analysis engines
|
||||
# do not like at all non existent objects when run from Linux (even if we handle properly
|
||||
# all the cases in the code).
|
||||
# So we access windll only by reflection to trick theses engines.
|
||||
if hasattr(ctypes, 'windll') and subcommand not in UNPRIVILEGED_SUBCOMMANDS_ALLOWED:
|
||||
windll = getattr(ctypes, 'windll')
|
||||
if windll.shell32.IsUserAnAdmin() == 0:
|
||||
raise errors.Error(
|
||||
'Error, "{0}" subcommand must be run on a shell with administrative rights.'
|
||||
.format(subcommand))
|
||||
|
||||
def os_geteuid():
|
||||
"""
|
||||
Get current user uid
|
||||
|
||||
:returns: The current user uid.
|
||||
:rtype: int
|
||||
|
||||
"""
|
||||
try:
|
||||
# Linux specific
|
||||
return os.geteuid()
|
||||
except AttributeError:
|
||||
# Windows specific
|
||||
return 0
|
||||
|
||||
def readline_with_timeout(timeout, prompt):
|
||||
"""
|
||||
Read user input to return the first line entered, or raise after specified timeout.
|
||||
|
||||
:param float timeout: The timeout in seconds given to the user.
|
||||
:param str prompt: The prompt message to display to the user.
|
||||
|
||||
:returns: The first line entered by the user.
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
try:
|
||||
# Linux specific
|
||||
#
|
||||
# Call to select can only be done like this on UNIX
|
||||
rlist, _, _ = select.select([sys.stdin], [], [], timeout)
|
||||
if not rlist:
|
||||
raise errors.Error(
|
||||
"Timed out waiting for answer to prompt '{0}'".format(prompt))
|
||||
return rlist[0].readline()
|
||||
except OSError:
|
||||
# Windows specific
|
||||
#
|
||||
# No way with select to make a timeout to the user input on Windows,
|
||||
# as select only supports socket in this case.
|
||||
# So no timeout on Windows for now.
|
||||
return sys.stdin.readline()
|
||||
|
||||
def lock_file(fd):
|
||||
"""
|
||||
Lock the file linked to the specified file descriptor.
|
||||
|
||||
:param int fd: The file descriptor of the file to lock.
|
||||
|
||||
"""
|
||||
if 'fcntl' in sys.modules:
|
||||
# Linux specific
|
||||
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
else:
|
||||
# Windows specific
|
||||
msvcrt.locking(fd, msvcrt.LK_NBLCK, 1)
|
||||
|
||||
def release_locked_file(fd, path):
|
||||
"""
|
||||
Remove, close, and release a lock file specified by its file descriptor and its path.
|
||||
|
||||
:param int fd: The file descriptor of the lock file.
|
||||
:param str path: The path of the lock file.
|
||||
|
||||
"""
|
||||
# Linux specific
|
||||
#
|
||||
# It is important the lock file is removed before it's released,
|
||||
# otherwise:
|
||||
#
|
||||
# process A: open lock file
|
||||
# process B: release lock file
|
||||
# process A: lock file
|
||||
# process A: check device and inode
|
||||
# process B: delete file
|
||||
# process C: open and lock a different file at the same path
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError as err:
|
||||
if err.errno == errno.EACCES:
|
||||
# Windows specific
|
||||
# We will not be able to remove a file before closing it.
|
||||
# To avoid race conditions described for Linux, we will not delete the lockfile,
|
||||
# just close it to be reused on the next Certbot call.
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
|
@ -25,6 +25,7 @@ from OpenSSL import SSL # type: ignore
|
|||
|
||||
from acme import crypto_util as acme_crypto_util
|
||||
from acme.magic_typing import IO # pylint: disable=unused-import, no-name-in-module
|
||||
from certbot import compat
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
from certbot import util
|
||||
|
|
@ -60,7 +61,7 @@ def init_save_key(key_size, key_dir, keyname="key-certbot.pem"):
|
|||
|
||||
config = zope.component.getUtility(interfaces.IConfig)
|
||||
# Save file
|
||||
util.make_or_verify_dir(key_dir, 0o700, os.geteuid(),
|
||||
util.make_or_verify_dir(key_dir, 0o700, compat.os_geteuid(),
|
||||
config.strict_permissions)
|
||||
key_f, key_path = util.unique_file(
|
||||
os.path.join(key_dir, keyname), 0o600, "wb")
|
||||
|
|
@ -91,7 +92,7 @@ def init_save_csr(privkey, names, path):
|
|||
privkey.pem, names, must_staple=config.must_staple)
|
||||
|
||||
# Save CSR
|
||||
util.make_or_verify_dir(path, 0o755, os.geteuid(),
|
||||
util.make_or_verify_dir(path, 0o755, compat.os_geteuid(),
|
||||
config.strict_permissions)
|
||||
csr_f, csr_filename = util.unique_file(
|
||||
os.path.join(path, "csr-certbot.pem"), 0o644, "wb")
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
"""Certbot display."""
|
||||
import logging
|
||||
import os
|
||||
import select
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import zope.interface
|
||||
|
||||
from certbot import compat
|
||||
from certbot import constants
|
||||
from certbot import interfaces
|
||||
from certbot import errors
|
||||
|
|
@ -79,13 +79,8 @@ def input_with_timeout(prompt=None, timeout=36000.0):
|
|||
sys.stdout.write(prompt)
|
||||
sys.stdout.flush()
|
||||
|
||||
# select can only be used like this on UNIX
|
||||
rlist, _, _ = select.select([sys.stdin], [], [], timeout)
|
||||
if not rlist:
|
||||
raise errors.Error(
|
||||
"Timed out waiting for answer to prompt '{0}'".format(prompt))
|
||||
line = compat.readline_with_timeout(timeout, prompt)
|
||||
|
||||
line = rlist[0].readline()
|
||||
if not line:
|
||||
raise EOFError
|
||||
return line.rstrip('\n')
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""Implements file locks for locking files and directories in UNIX."""
|
||||
import errno
|
||||
import fcntl
|
||||
import logging
|
||||
import os
|
||||
|
||||
from certbot import compat
|
||||
from certbot import errors
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
@ -74,7 +74,7 @@ class LockFile(object):
|
|||
|
||||
"""
|
||||
try:
|
||||
fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
compat.lock_file(fd)
|
||||
except IOError as err:
|
||||
if err.errno in (errno.EACCES, errno.EAGAIN):
|
||||
logger.debug(
|
||||
|
|
@ -118,22 +118,7 @@ class LockFile(object):
|
|||
|
||||
def release(self):
|
||||
"""Remove, close, and release the lock file."""
|
||||
# It is important the lock file is removed before it's released,
|
||||
# otherwise:
|
||||
#
|
||||
# process A: open lock file
|
||||
# process B: release lock file
|
||||
# process A: lock file
|
||||
# process A: check device and inode
|
||||
# process B: delete file
|
||||
# process C: open and lock a different file at the same path
|
||||
#
|
||||
# Calling os.remove on a file that's in use doesn't work on
|
||||
# Windows, but neither does locking with fcntl.
|
||||
try:
|
||||
os.remove(self._path)
|
||||
compat.release_locked_file(self._fd, self._path)
|
||||
finally:
|
||||
try:
|
||||
os.close(self._fd)
|
||||
finally:
|
||||
self._fd = None
|
||||
self._fd = None
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import traceback
|
|||
|
||||
from acme import messages
|
||||
|
||||
from certbot import compat
|
||||
from certbot import constants
|
||||
from certbot import errors
|
||||
from certbot import util
|
||||
|
|
@ -133,7 +134,7 @@ def setup_log_file_handler(config, logfile, fmt):
|
|||
# TODO: logs might contain sensitive data such as contents of the
|
||||
# private key! #525
|
||||
util.set_up_core_dir(
|
||||
config.logs_dir, 0o700, os.geteuid(), config.strict_permissions)
|
||||
config.logs_dir, 0o700, compat.os_geteuid(), config.strict_permissions)
|
||||
log_file_path = os.path.join(config.logs_dir, logfile)
|
||||
try:
|
||||
handler = logging.handlers.RotatingFileHandler(
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from certbot import account
|
|||
from certbot import cert_manager
|
||||
from certbot import cli
|
||||
from certbot import client
|
||||
from certbot import compat
|
||||
from certbot import configuration
|
||||
from certbot import constants
|
||||
from certbot import crypto_util
|
||||
|
|
@ -531,8 +532,7 @@ def _determine_account(config):
|
|||
|
||||
def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-branches
|
||||
"""Does the user want to delete their now-revoked certs? If run in non-interactive mode,
|
||||
deleting happens automatically, unless if both `--cert-name` and `--cert-path` were
|
||||
specified with conflicting values.
|
||||
deleting happens automatically.
|
||||
|
||||
:param config: parsed command line arguments
|
||||
:type config: interfaces.IConfig
|
||||
|
|
@ -556,50 +556,13 @@ def _delete_if_appropriate(config): # pylint: disable=too-many-locals,too-many-b
|
|||
reporter_util.add_message("Not deleting revoked certs.", reporter_util.LOW_PRIORITY)
|
||||
return
|
||||
|
||||
if not (config.certname or config.cert_path):
|
||||
raise errors.Error('At least one of --cert-path or --cert-name must be specified.')
|
||||
# config.cert_path must have been set
|
||||
# config.certname may have been set
|
||||
assert config.cert_path
|
||||
|
||||
if config.certname and config.cert_path:
|
||||
# first, check if certname and cert_path imply the same certs
|
||||
implied_cert_name = cert_manager.cert_path_to_lineage(config)
|
||||
|
||||
if implied_cert_name != config.certname:
|
||||
cert_path_implied_cert_name = cert_manager.cert_path_to_lineage(config)
|
||||
cert_path_implied_conf = storage.renewal_file_for_certname(config,
|
||||
cert_path_implied_cert_name)
|
||||
cert_path_cert = storage.RenewableCert(cert_path_implied_conf, config)
|
||||
cert_path_info = cert_manager.human_readable_cert_info(config, cert_path_cert,
|
||||
skip_filter_checks=True)
|
||||
|
||||
cert_name_implied_conf = storage.renewal_file_for_certname(config, config.certname)
|
||||
cert_name_cert = storage.RenewableCert(cert_name_implied_conf, config)
|
||||
cert_name_info = cert_manager.human_readable_cert_info(config, cert_name_cert)
|
||||
|
||||
msg = ("You specified conflicting values for --cert-path and --cert-name. "
|
||||
"Which did you mean to select?")
|
||||
choices = [cert_path_info, cert_name_info]
|
||||
try:
|
||||
code, index = display.menu(msg,
|
||||
choices, ok_label="Select", force_interactive=True)
|
||||
except errors.MissingCommandlineFlag:
|
||||
error_msg = ('To run in non-interactive mode, you must either specify only one of '
|
||||
'--cert-path or --cert-name, or both must point to the same certificate lineages.')
|
||||
raise errors.Error(error_msg)
|
||||
|
||||
if code != display_util.OK or not index in range(0, len(choices)):
|
||||
raise errors.Error("User ended interaction.")
|
||||
|
||||
if index == 0:
|
||||
config.certname = cert_path_implied_cert_name
|
||||
else:
|
||||
config.cert_path = storage.cert_path_for_cert_name(config, config.certname)
|
||||
|
||||
elif config.cert_path:
|
||||
if not config.certname:
|
||||
config.certname = cert_manager.cert_path_to_lineage(config)
|
||||
|
||||
else: # if only config.certname was specified
|
||||
config.cert_path = storage.cert_path_for_cert_name(config, config.certname)
|
||||
|
||||
# don't delete if the archive_dir is used by some other lineage
|
||||
archive_dir = storage.full_archive_path(
|
||||
configobj.ConfigObj(storage.renewal_file_for_certname(config, config.certname)),
|
||||
|
|
@ -1065,6 +1028,14 @@ def revoke(config, unused_plugins): # TODO: coop with renewal config
|
|||
"""
|
||||
# For user-agent construction
|
||||
config.installer = config.authenticator = None
|
||||
|
||||
if config.cert_path is None and config.certname:
|
||||
config.cert_path = storage.cert_path_for_cert_name(config, config.certname)
|
||||
elif not config.cert_path or (config.cert_path and config.certname):
|
||||
# intentionally not supporting --cert-path & --cert-name together,
|
||||
# to avoid dealing with mismatched values
|
||||
raise errors.Error("Error! Exactly one of --cert-path or --cert-name must be specified!")
|
||||
|
||||
if config.key_path is not None: # revocation by cert key
|
||||
logger.debug("Revoking %s using cert key %s",
|
||||
config.cert_path[0], config.key_path[0])
|
||||
|
|
@ -1077,7 +1048,6 @@ def revoke(config, unused_plugins): # TODO: coop with renewal config
|
|||
acme = client.acme_from_config_key(config, acc.key, acc.regr)
|
||||
cert = crypto_util.pyopenssl_load_certificate(config.cert_path[1])[0]
|
||||
logger.debug("Reason code for revocation: %s", config.reason)
|
||||
|
||||
try:
|
||||
acme.revoke(jose.ComparableX509(cert), config.reason)
|
||||
_delete_if_appropriate(config)
|
||||
|
|
@ -1289,16 +1259,16 @@ def make_or_verify_needed_dirs(config):
|
|||
|
||||
"""
|
||||
util.set_up_core_dir(config.config_dir, constants.CONFIG_DIRS_MODE,
|
||||
os.geteuid(), config.strict_permissions)
|
||||
compat.os_geteuid(), config.strict_permissions)
|
||||
util.set_up_core_dir(config.work_dir, constants.CONFIG_DIRS_MODE,
|
||||
os.geteuid(), config.strict_permissions)
|
||||
compat.os_geteuid(), config.strict_permissions)
|
||||
|
||||
hook_dirs = (config.renewal_pre_hooks_dir,
|
||||
config.renewal_deploy_hooks_dir,
|
||||
config.renewal_post_hooks_dir,)
|
||||
for hook_dir in hook_dirs:
|
||||
util.make_or_verify_dir(hook_dir,
|
||||
uid=os.geteuid(),
|
||||
uid=compat.os_geteuid(),
|
||||
strict=config.strict_permissions)
|
||||
|
||||
|
||||
|
|
@ -1333,6 +1303,7 @@ def main(cli_args=sys.argv[1:]):
|
|||
:raises errors.Error: error if plugin command is not supported
|
||||
|
||||
"""
|
||||
|
||||
log.pre_arg_parse_setup()
|
||||
|
||||
plugins = plugins_disco.PluginsRegistry.find_all()
|
||||
|
|
@ -1346,6 +1317,10 @@ def main(cli_args=sys.argv[1:]):
|
|||
config = configuration.NamespaceConfig(args)
|
||||
zope.component.provideUtility(config)
|
||||
|
||||
# On windows, shell without administrative right cannot create symlinks required by certbot.
|
||||
# So we check the rights before continuing.
|
||||
compat.raise_for_non_administrative_windows_rights(config.verb)
|
||||
|
||||
try:
|
||||
log.post_arg_parse_setup(config)
|
||||
make_or_verify_needed_dirs(config)
|
||||
|
|
|
|||
|
|
@ -9,18 +9,19 @@ logger = logging.getLogger(__name__)
|
|||
def get_prefixes(path):
|
||||
"""Retrieves all possible path prefixes of a path, in descending order
|
||||
of length. For instance,
|
||||
/a/b/c/ => ['/a/b/c/', '/a/b/c', '/a/b', '/a', '/']
|
||||
(linux) /a/b/c returns ['/a/b/c', '/a/b', '/a', '/']
|
||||
(windows) C:\\a\\b\\c returns ['C:\\a\\b\\c', 'C:\\a\\b', 'C:\\a', 'C:']
|
||||
:param str path: the path to break into prefixes
|
||||
|
||||
:returns: all possible path prefixes of given path in descending order
|
||||
:rtype: `list` of `str`
|
||||
"""
|
||||
prefix = path
|
||||
prefix = os.path.normpath(path)
|
||||
prefixes = []
|
||||
while len(prefix) > 0:
|
||||
prefixes.append(prefix)
|
||||
prefix, _ = os.path.split(prefix)
|
||||
# break once we hit '/'
|
||||
# break once we hit the root path
|
||||
if prefix == prefixes[-1]:
|
||||
break
|
||||
return prefixes
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ class GetPrefixTest(unittest.TestCase):
|
|||
"""Tests for certbot.plugins.get_prefixes."""
|
||||
def test_get_prefix(self):
|
||||
from certbot.plugins.util import get_prefixes
|
||||
self.assertEqual(get_prefixes("/a/b/c/"), ['/a/b/c/', '/a/b/c', '/a/b', '/a', '/'])
|
||||
self.assertEqual(get_prefixes("/"), ["/"])
|
||||
self.assertEqual(get_prefixes("a"), ["a"])
|
||||
self.assertEqual(get_prefixes('/a/b/c'), ['/a/b/c', '/a/b', '/a', '/'])
|
||||
self.assertEqual(get_prefixes('/'), ['/'])
|
||||
self.assertEqual(get_prefixes('a'), ['a'])
|
||||
|
||||
class PathSurgeryTest(unittest.TestCase):
|
||||
"""Tests for certbot.plugins.path_surgery."""
|
||||
|
|
|
|||
|
|
@ -170,7 +170,9 @@ to serve all files under specified web root ({0})."""
|
|||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
stat_path = os.stat(path)
|
||||
for prefix in sorted(util.get_prefixes(self.full_roots[name]), key=len):
|
||||
# We ignore the last prefix in the next iteration,
|
||||
# as it does not correspond to a folder path ('/' or 'C:')
|
||||
for prefix in sorted(util.get_prefixes(self.full_roots[name])[:-1], key=len):
|
||||
try:
|
||||
# This is coupled with the "umask" call above because
|
||||
# os.mkdir's "mode" parameter may not always work:
|
||||
|
|
@ -180,7 +182,7 @@ to serve all files under specified web root ({0})."""
|
|||
# Set owner as parent directory if possible
|
||||
try:
|
||||
os.chown(prefix, stat_path.st_uid, stat_path.st_gid)
|
||||
except OSError as exception:
|
||||
except (OSError, AttributeError) as exception:
|
||||
logger.info("Unable to change owner and uid of webroot directory")
|
||||
logger.debug("Error was: %s", exception)
|
||||
except OSError as exception:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import traceback
|
|||
import six
|
||||
import zope.component
|
||||
|
||||
from certbot import compat
|
||||
from certbot import constants
|
||||
from certbot import errors
|
||||
from certbot import interfaces
|
||||
|
|
@ -65,7 +66,7 @@ class Reverter(object):
|
|||
self.config = config
|
||||
|
||||
util.make_or_verify_dir(
|
||||
config.backup_dir, constants.CONFIG_DIRS_MODE, os.geteuid(),
|
||||
config.backup_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(),
|
||||
self.config.strict_permissions)
|
||||
|
||||
def revert_temporary_config(self):
|
||||
|
|
@ -219,7 +220,7 @@ class Reverter(object):
|
|||
|
||||
"""
|
||||
util.make_or_verify_dir(
|
||||
cp_dir, constants.CONFIG_DIRS_MODE, os.geteuid(),
|
||||
cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(),
|
||||
self.config.strict_permissions)
|
||||
|
||||
op_fd, existing_filepaths = self._read_and_append(
|
||||
|
|
@ -433,7 +434,7 @@ class Reverter(object):
|
|||
cp_dir = self.config.in_progress_dir
|
||||
|
||||
util.make_or_verify_dir(
|
||||
cp_dir, constants.CONFIG_DIRS_MODE, os.geteuid(),
|
||||
cp_dir, constants.CONFIG_DIRS_MODE, compat.os_geteuid(),
|
||||
self.config.strict_permissions)
|
||||
|
||||
return cp_dir
|
||||
|
|
|
|||
|
|
@ -48,18 +48,23 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
|
|||
mock_constants.TEMP_CHECKPOINT_DIR = 't'
|
||||
|
||||
self.assertEqual(
|
||||
self.config.accounts_dir, os.path.join(
|
||||
self.config.config_dir, 'acc/acme-server.org:443/new'))
|
||||
os.path.normpath(self.config.accounts_dir),
|
||||
os.path.normpath(os.path.join(self.config.config_dir, 'acc/acme-server.org:443/new')))
|
||||
self.assertEqual(
|
||||
self.config.backup_dir, os.path.join(self.config.work_dir, 'backups'))
|
||||
os.path.normpath(self.config.backup_dir),
|
||||
os.path.normpath(os.path.join(self.config.work_dir, 'backups')))
|
||||
self.assertEqual(
|
||||
self.config.csr_dir, os.path.join(self.config.config_dir, 'csr'))
|
||||
os.path.normpath(self.config.csr_dir),
|
||||
os.path.normpath(os.path.join(self.config.config_dir, 'csr')))
|
||||
self.assertEqual(
|
||||
self.config.in_progress_dir, os.path.join(self.config.work_dir, '../p'))
|
||||
os.path.normpath(self.config.in_progress_dir),
|
||||
os.path.normpath(os.path.join(self.config.work_dir, '../p')))
|
||||
self.assertEqual(
|
||||
self.config.key_dir, os.path.join(self.config.config_dir, 'keys'))
|
||||
os.path.normpath(self.config.key_dir),
|
||||
os.path.normpath(os.path.join(self.config.config_dir, 'keys')))
|
||||
self.assertEqual(
|
||||
self.config.temp_checkpoint_dir, os.path.join(self.config.work_dir, 't'))
|
||||
os.path.normpath(self.config.temp_checkpoint_dir),
|
||||
os.path.normpath(os.path.join(self.config.work_dir, 't')))
|
||||
|
||||
def test_absolute_paths(self):
|
||||
from certbot.configuration import NamespaceConfig
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class InputWithTimeoutTest(unittest.TestCase):
|
|||
def test_input(self, prompt=None):
|
||||
expected = "foo bar"
|
||||
stdin = six.StringIO(expected + "\n")
|
||||
with mock.patch("certbot.display.util.select.select") as mock_select:
|
||||
with mock.patch("certbot.compat.select.select") as mock_select:
|
||||
mock_select.return_value = ([stdin], [], [],)
|
||||
self.assertEqual(self._call(prompt), expected)
|
||||
|
||||
|
|
@ -321,11 +321,7 @@ class FileOutputDisplayTest(unittest.TestCase):
|
|||
|
||||
|
||||
class NoninteractiveDisplayTest(unittest.TestCase):
|
||||
"""Test non-interactive display.
|
||||
|
||||
These tests are pretty easy!
|
||||
|
||||
"""
|
||||
"""Test non-interactive display. These tests are pretty easy!"""
|
||||
def setUp(self):
|
||||
super(NoninteractiveDisplayTest, self).setUp()
|
||||
self.mock_stdout = mock.MagicMock()
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class LockFileTest(test_util.TempDirTestCase):
|
|||
lock_file.release()
|
||||
self.assertFalse(os.path.exists(self.lock_path))
|
||||
|
||||
@mock.patch('certbot.lock.fcntl.lockf')
|
||||
@mock.patch('certbot.compat.fcntl.lockf')
|
||||
def test_unexpected_lockf_err(self, mock_lockf):
|
||||
msg = 'hi there'
|
||||
mock_lockf.side_effect = IOError(msg)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ from six.moves import reload_module # pylint: disable=import-error
|
|||
from acme.magic_typing import List # pylint: disable=unused-import, no-name-in-module
|
||||
from certbot import account
|
||||
from certbot import cli
|
||||
from certbot import compat
|
||||
from certbot import constants
|
||||
from certbot import configuration
|
||||
from certbot import crypto_util
|
||||
|
|
@ -239,6 +240,8 @@ class RevokeTest(test_util.TempDirTestCase):
|
|||
shutil.copy(CERT_PATH, self.tempdir)
|
||||
self.tmp_cert_path = os.path.abspath(os.path.join(self.tempdir,
|
||||
'cert_512.pem'))
|
||||
with open(self.tmp_cert_path, 'r') as f:
|
||||
self.tmp_cert = (self.tmp_cert_path, f.read())
|
||||
|
||||
self.patches = [
|
||||
mock.patch('acme.client.BackwardsCompatibleClientV2'),
|
||||
|
|
@ -268,9 +271,10 @@ class RevokeTest(test_util.TempDirTestCase):
|
|||
for patch in self.patches:
|
||||
patch.stop()
|
||||
|
||||
def _call(self, extra_args=""):
|
||||
args = 'revoke --cert-path={0} ' + extra_args
|
||||
args = args.format(self.tmp_cert_path).split()
|
||||
def _call(self, args=None):
|
||||
if not args:
|
||||
args = 'revoke --cert-path={0} '
|
||||
args = args.format(self.tmp_cert_path).split()
|
||||
plugins = disco.PluginsRegistry.find_all()
|
||||
config = configuration.NamespaceConfig(
|
||||
cli.prepare_and_parse_args(plugins, args))
|
||||
|
|
@ -286,12 +290,25 @@ class RevokeTest(test_util.TempDirTestCase):
|
|||
mock_revoke = mock_acme_client.BackwardsCompatibleClientV2().revoke
|
||||
expected = []
|
||||
for reason, code in constants.REVOCATION_REASONS.items():
|
||||
self._call("--reason " + reason)
|
||||
args = 'revoke --cert-path={0} --reason {1}'.format(self.tmp_cert_path, reason).split()
|
||||
self._call(args)
|
||||
expected.append(mock.call(mock.ANY, code))
|
||||
self._call("--reason " + reason.upper())
|
||||
args = 'revoke --cert-path={0} --reason {1}'.format(self.tmp_cert_path,
|
||||
reason.upper()).split()
|
||||
self._call(args)
|
||||
expected.append(mock.call(mock.ANY, code))
|
||||
self.assertEqual(expected, mock_revoke.call_args_list)
|
||||
|
||||
@mock.patch('certbot.main._delete_if_appropriate')
|
||||
@mock.patch('certbot.storage.cert_path_for_cert_name')
|
||||
def test_revoke_by_certname(self, mock_cert_path_for_cert_name,
|
||||
mock_delete_if_appropriate):
|
||||
args = 'revoke --cert-name=example.com'.split()
|
||||
mock_cert_path_for_cert_name.return_value = self.tmp_cert
|
||||
mock_delete_if_appropriate.return_value = False
|
||||
self._call(args)
|
||||
self.mock_success_revoke.assert_called_once_with(self.tmp_cert_path)
|
||||
|
||||
@mock.patch('certbot.main._delete_if_appropriate')
|
||||
def test_revocation_success(self, mock_delete_if_appropriate):
|
||||
self._call()
|
||||
|
|
@ -358,25 +375,6 @@ class DeleteIfAppropriateTest(test_util.ConfigTestCase):
|
|||
self._call(config)
|
||||
mock_delete.assert_not_called()
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
@mock.patch('certbot.storage.renewal_file_for_certname')
|
||||
@mock.patch('certbot.cert_manager.match_and_check_overlaps')
|
||||
@mock.patch('certbot.storage.full_archive_path')
|
||||
@mock.patch('certbot.cert_manager.delete')
|
||||
@mock.patch('certbot.storage.cert_path_for_cert_name')
|
||||
@test_util.patch_get_utility()
|
||||
def test_cert_name_only(self, mock_get_utility,
|
||||
mock_cert_path_for_cert_name, mock_delete, mock_archive,
|
||||
mock_overlapping_archive_dirs, mock_renewal_file_for_certname):
|
||||
# pylint: disable = unused-argument
|
||||
config = self.config
|
||||
config.certname = "example.com"
|
||||
config.cert_path = ""
|
||||
mock_cert_path_for_cert_name.return_value = "/some/reasonable/path"
|
||||
mock_overlapping_archive_dirs.return_value = False
|
||||
self._call(config)
|
||||
self.assertEqual(mock_delete.call_count, 1)
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
@mock.patch('certbot.storage.renewal_file_for_certname')
|
||||
@mock.patch('certbot.cert_manager.match_and_check_overlaps')
|
||||
|
|
@ -439,89 +437,6 @@ class DeleteIfAppropriateTest(test_util.ConfigTestCase):
|
|||
self.assertEqual(mock_delete.call_count, 1)
|
||||
self.assertFalse(mock_get_utility().yesno.called)
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
@mock.patch('certbot.storage.renewal_file_for_certname')
|
||||
@mock.patch('certbot.cert_manager.match_and_check_overlaps')
|
||||
@mock.patch('certbot.storage.full_archive_path')
|
||||
@mock.patch('certbot.cert_manager.delete')
|
||||
@mock.patch('certbot.cert_manager.cert_path_to_lineage')
|
||||
@test_util.patch_get_utility()
|
||||
def test_certname_and_cert_path_match(self, mock_get_utility,
|
||||
mock_cert_path_to_lineage, mock_delete, mock_archive,
|
||||
mock_overlapping_archive_dirs, mock_renewal_file_for_certname):
|
||||
# pylint: disable = unused-argument
|
||||
config = self.config
|
||||
config.certname = "example.com"
|
||||
config.cert_path = "/some/reasonable/path"
|
||||
mock_cert_path_to_lineage.return_value = config.certname
|
||||
mock_overlapping_archive_dirs.return_value = False
|
||||
self._call(config)
|
||||
self.assertEqual(mock_delete.call_count, 1)
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
@mock.patch('certbot.cert_manager.match_and_check_overlaps')
|
||||
@mock.patch('certbot.storage.full_archive_path')
|
||||
@mock.patch('certbot.cert_manager.delete')
|
||||
@mock.patch('certbot.cert_manager.human_readable_cert_info')
|
||||
@mock.patch('certbot.storage.RenewableCert')
|
||||
@mock.patch('certbot.storage.renewal_file_for_certname')
|
||||
@mock.patch('certbot.cert_manager.cert_path_to_lineage')
|
||||
@test_util.patch_get_utility()
|
||||
def test_certname_and_cert_path_mismatch(self, mock_get_utility,
|
||||
mock_cert_path_to_lineage, mock_renewal_file_for_certname,
|
||||
mock_RenewableCert, mock_human_readable_cert_info,
|
||||
mock_delete, mock_archive, mock_overlapping_archive_dirs):
|
||||
# pylint: disable=unused-argument
|
||||
config = self.config
|
||||
config.certname = "example.com"
|
||||
config.cert_path = "/some/reasonable/path"
|
||||
mock_cert_path_to_lineage = "something else"
|
||||
mock_RenewableCert.return_value = mock.Mock()
|
||||
mock_human_readable_cert_info.return_value = ""
|
||||
mock_overlapping_archive_dirs.return_value = False
|
||||
from certbot.display import util as display_util
|
||||
util_mock = mock_get_utility()
|
||||
util_mock.menu.return_value = (display_util.OK, 0)
|
||||
self._call(config)
|
||||
self.assertEqual(mock_delete.call_count, 1)
|
||||
|
||||
# pylint: disable=too-many-arguments
|
||||
@mock.patch('certbot.cert_manager.match_and_check_overlaps')
|
||||
@mock.patch('certbot.storage.full_archive_path')
|
||||
@mock.patch('certbot.cert_manager.delete')
|
||||
@mock.patch('certbot.cert_manager.human_readable_cert_info')
|
||||
@mock.patch('certbot.storage.RenewableCert')
|
||||
@mock.patch('certbot.storage.renewal_file_for_certname')
|
||||
@mock.patch('certbot.cert_manager.cert_path_to_lineage')
|
||||
@test_util.patch_get_utility()
|
||||
def test_noninteractive_certname_cert_path_mismatch(self, mock_get_utility,
|
||||
mock_cert_path_to_lineage, mock_renewal_file_for_certname,
|
||||
mock_RenewableCert, mock_human_readable_cert_info,
|
||||
mock_delete, mock_archive, mock_overlapping_archive_dirs):
|
||||
# pylint: disable=unused-argument
|
||||
config = self.config
|
||||
config.certname = "example.com"
|
||||
config.cert_path = "/some/reasonable/path"
|
||||
mock_cert_path_to_lineage.return_value = "some-reasonable-path.com"
|
||||
mock_RenewableCert.return_value = mock.Mock()
|
||||
mock_human_readable_cert_info.return_value = ""
|
||||
mock_overlapping_archive_dirs.return_value = False
|
||||
# Test for non-interactive mode
|
||||
util_mock = mock_get_utility()
|
||||
util_mock.menu.side_effect = errors.MissingCommandlineFlag("Oh no.")
|
||||
self.assertRaises(errors.Error, self._call, config)
|
||||
mock_delete.assert_not_called()
|
||||
|
||||
@mock.patch('certbot.cert_manager.delete')
|
||||
@test_util.patch_get_utility()
|
||||
def test_no_certname_or_cert_path(self, mock_get_utility, mock_delete):
|
||||
# pylint: disable=unused-argument
|
||||
config = self.config
|
||||
config.certname = None
|
||||
config.cert_path = None
|
||||
self.assertRaises(errors.Error, self._call, config)
|
||||
mock_delete.assert_not_called()
|
||||
|
||||
|
||||
class DetermineAccountTest(test_util.ConfigTestCase):
|
||||
"""Tests for certbot.main._determine_account."""
|
||||
|
|
@ -1577,7 +1492,7 @@ class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
|
|||
for core_dir in (self.config.config_dir, self.config.work_dir,):
|
||||
mock_util.set_up_core_dir.assert_any_call(
|
||||
core_dir, constants.CONFIG_DIRS_MODE,
|
||||
os.geteuid(), self.config.strict_permissions
|
||||
compat.os_geteuid(), self.config.strict_permissions
|
||||
)
|
||||
|
||||
hook_dirs = (self.config.renewal_pre_hooks_dir,
|
||||
|
|
@ -1586,7 +1501,7 @@ class MakeOrVerifyNeededDirs(test_util.ConfigTestCase):
|
|||
for hook_dir in hook_dirs:
|
||||
# default mode of 755 is used
|
||||
mock_util.make_or_verify_dir.assert_any_call(
|
||||
hook_dir, uid=os.geteuid(),
|
||||
hook_dir, uid=compat.os_geteuid(),
|
||||
strict=self.config.strict_permissions)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -362,7 +362,6 @@ def lock_and_call(func, lock_path):
|
|||
child.join()
|
||||
assert child.exitcode == 0
|
||||
|
||||
|
||||
def hold_lock(cv, lock_path): # pragma: no cover
|
||||
"""Acquire a file lock at lock_path and wait to release it.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mock
|
|||
import six
|
||||
from six.moves import reload_module # pylint: disable=import-error
|
||||
|
||||
from certbot import compat
|
||||
from certbot import errors
|
||||
import certbot.tests.util as test_util
|
||||
|
||||
|
|
@ -116,7 +117,7 @@ class SetUpCoreDirTest(test_util.TempDirTestCase):
|
|||
@mock.patch('certbot.util.lock_dir_until_exit')
|
||||
def test_success(self, mock_lock):
|
||||
new_dir = os.path.join(self.tempdir, 'new')
|
||||
self._call(new_dir, 0o700, os.geteuid(), False)
|
||||
self._call(new_dir, 0o700, compat.os_geteuid(), False)
|
||||
self.assertTrue(os.path.exists(new_dir))
|
||||
self.assertEqual(mock_lock.call_count, 1)
|
||||
|
||||
|
|
@ -124,7 +125,7 @@ class SetUpCoreDirTest(test_util.TempDirTestCase):
|
|||
def test_failure(self, mock_make_or_verify):
|
||||
mock_make_or_verify.side_effect = OSError
|
||||
self.assertRaises(errors.Error, self._call,
|
||||
self.tempdir, 0o700, os.geteuid(), False)
|
||||
self.tempdir, 0o700, compat.os_geteuid(), False)
|
||||
|
||||
|
||||
class MakeOrVerifyDirTest(test_util.TempDirTestCase):
|
||||
|
|
|
|||
|
|
@ -108,7 +108,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.26.1
|
||||
"". (default: CertbotACMEClient/0.27.1
|
||||
(certbot(-auto); OS_NAME OS_VERSION) Authenticator/XXX
|
||||
Installer/YYY (SUBCOMMAND; flags: FLAGS)
|
||||
Py/major.minor.patchlevel). The flags encoded in the
|
||||
|
|
@ -475,16 +475,15 @@ apache:
|
|||
Apache Web Server plugin - Beta
|
||||
|
||||
--apache-enmod APACHE_ENMOD
|
||||
Path to the Apache 'a2enmod' binary. (default:
|
||||
a2enmod)
|
||||
Path to the Apache 'a2enmod' binary (default: a2enmod)
|
||||
--apache-dismod APACHE_DISMOD
|
||||
Path to the Apache 'a2dismod' binary. (default:
|
||||
Path to the Apache 'a2dismod' binary (default:
|
||||
a2dismod)
|
||||
--apache-le-vhost-ext APACHE_LE_VHOST_EXT
|
||||
SSL vhost configuration extension. (default: -le-
|
||||
SSL vhost configuration extension (default: -le-
|
||||
ssl.conf)
|
||||
--apache-server-root APACHE_SERVER_ROOT
|
||||
Apache server root directory. (default: /etc/apache2)
|
||||
Apache server root directory (default: /etc/apache2)
|
||||
--apache-vhost-root APACHE_VHOST_ROOT
|
||||
Apache server VirtualHost configuration root (default:
|
||||
None)
|
||||
|
|
@ -492,14 +491,17 @@ apache:
|
|||
Apache server logs directory (default:
|
||||
/var/log/apache2)
|
||||
--apache-challenge-location APACHE_CHALLENGE_LOCATION
|
||||
Directory path for challenge configuration. (default:
|
||||
Directory path for challenge configuration (default:
|
||||
/etc/apache2)
|
||||
--apache-handle-modules APACHE_HANDLE_MODULES
|
||||
Let installer handle enabling required modules for
|
||||
you. (Only Ubuntu/Debian currently) (default: True)
|
||||
Let installer handle enabling required modules for you
|
||||
(Only Ubuntu/Debian currently) (default: True)
|
||||
--apache-handle-sites APACHE_HANDLE_SITES
|
||||
Let installer handle enabling sites for you. (Only
|
||||
Let installer handle enabling sites for you (Only
|
||||
Ubuntu/Debian currently) (default: True)
|
||||
--apache-ctl APACHE_CTL
|
||||
Full path to Apache control script (default:
|
||||
apache2ctl)
|
||||
|
||||
certbot-route53:auth:
|
||||
Obtain certificates using a DNS TXT record (if you are using AWS Route53
|
||||
|
|
|
|||
|
|
@ -190,10 +190,11 @@ If you'd like to obtain a wildcard certificate from Let's Encrypt or run
|
|||
``certbot`` on a machine other than your target webserver, you can use one of
|
||||
Certbot's DNS plugins.
|
||||
|
||||
These plugins are still in the process of being packaged
|
||||
by many distributions and cannot currently be installed with ``certbot-auto``.
|
||||
If, however, you are comfortable installing the certificates yourself,
|
||||
you can run these plugins with :ref:`Docker <docker-user>`.
|
||||
These plugins are not included in a default Certbot installation and must be
|
||||
installed separately. While the DNS plugins cannot currently be used with
|
||||
``certbot-auto``, they are available in many OS package managers and as Docker
|
||||
images. Visit https://certbot.eff.org to learn the best way to use the DNS
|
||||
plugins on your system.
|
||||
|
||||
Once installed, you can find documentation on how to use each plugin at:
|
||||
|
||||
|
|
@ -904,7 +905,7 @@ Lock Files
|
|||
|
||||
When processing a validation Certbot writes a number of lock files on your system
|
||||
to prevent multiple instances from overwriting each other's changes. This means
|
||||
that be default two instances of Certbot will not be able to run in parallel.
|
||||
that by default two instances of Certbot will not be able to run in parallel.
|
||||
|
||||
Since the directories used by Certbot are configurable, Certbot
|
||||
will write a lock file for all of the directories it uses. This include Certbot's
|
||||
|
|
|
|||
|
|
@ -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.26.1"
|
||||
LE_AUTO_VERSION="0.27.1"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
|
|
@ -1197,18 +1197,18 @@ letsencrypt==0.7.0 \
|
|||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.26.1 \
|
||||
--hash=sha256:4e2ffdeebb7f5097600bcb1ca19131441fa021f952b443ca7454a279337af609 \
|
||||
--hash=sha256:4983513d63f7f36e24a07873ca2d6ea1c0101aa6cb1cd825cda02ed520f6ca66
|
||||
acme==0.26.1 \
|
||||
--hash=sha256:d47841e66adc1336ecca2f0d41a247c1b62307c981be6d07996bbf3f95af1dc5 \
|
||||
--hash=sha256:86e7b5f4654cb19215f16c0e6225750db7421f68ef6a0a040a61796f24e690be
|
||||
certbot-apache==0.26.1 \
|
||||
--hash=sha256:c16acb49bd4f84fff25bcbb7eaf74412145efe9b68ce46e1803be538894f2ce3 \
|
||||
--hash=sha256:b7fa327e987b892d64163e7519bdeaf9723d78275ef6c438272848894ace6d87
|
||||
certbot-nginx==0.26.1 \
|
||||
--hash=sha256:c0048dc83672dc90805a8ddf513be3e48c841d6e91607e91e8657c1785d65660 \
|
||||
--hash=sha256:d0c95a32625e0f1612d7fcf9021e6e050ba3d879823489d1edd2478a78ae6624
|
||||
certbot==0.27.1 \
|
||||
--hash=sha256:89a8d8e44e272ee970259c93fa2ff2c9f063da8fd88a56d7ca30d7a2218791ea \
|
||||
--hash=sha256:3570bd14ed223c752f309dbd082044bd9f11a339d21671e70a2eeae4e51ed02a
|
||||
acme==0.27.1 \
|
||||
--hash=sha256:0d42cfc9050a2e1d6d4e6b66334df8173778db0b3fe7a2b3bcb58f7034913597 \
|
||||
--hash=sha256:31a7b9023ce183616e6ebd5d783e842c3d68696ff70db59a06db9feea8f54f90
|
||||
certbot-apache==0.27.1 \
|
||||
--hash=sha256:1c73297e6a59cebcf5f5692025d4013ccd02c858bdc946fee3c6613f62bb9414 \
|
||||
--hash=sha256:61d6d706d49d726b53a831a2ea9099bd6c02657ff537a166dd197cd5f494d854
|
||||
certbot-nginx==0.27.1 \
|
||||
--hash=sha256:9772198bcfde9b68e448c15c3801b3cf9d20eb9ea9da1d9f4f9a7692b0fc2314 \
|
||||
--hash=sha256:ff5b849a9b4e3d1fd50ea351a1393738382fc9bd47bc5ac18c343d11a691349f
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v2
|
||||
|
||||
iQEcBAABCAAGBQJbTSv8AAoJEE0XyZXNl3Xy12sH/1FgV3SDVG0T1jgKQOYEUwrq
|
||||
cmpjdav8YPgFOSQDOcyFZG0DNcRfTskZt45IMkBLLnXq2PuPvkppc1+akP81vMoK
|
||||
NXHHS+PXDMjnBW4NFkexoM06KRF1SyHnvqsOg13w7UW2CjsAgtazGF5BucNCnjPH
|
||||
XJTwUf4uhKxeUb0Xkva1OPH++oTWz8+SYgWr/iMggkBrK8y04QUUJ6lyCO6MZgcE
|
||||
3JcECG7CwMK+hW0gCUkCSNZ0NzOBALCd9wCxNGszgkeJXrrW73oUpZmGC5BxIwYY
|
||||
o6lcF0qo7Jb92t4B3+7JhulMC5JoVoG4lpiXpKQFFCT0P4pZKotIomKNMATmnB4=
|
||||
=hzUL
|
||||
iQEzBAABCAAdFiEEos+1H6J1pyhiNOeyTRfJlc2XdfIFAluRtuUACgkQTRfJlc2X
|
||||
dfIvhgf7BrKDo9wjHU8Yb2h1O63OJmoYSQMqM4Q44OVkTTjHQZgDYrOflbegq9g+
|
||||
nxxOcMakiPTxvefZOecczKGTZZ/S+A/w5kH/9vJbxW0277iNnYsj1G59m1UPNzgn
|
||||
ECFL5AUKhl/RF3NWSpe2XhGA7ybls8LAidwxeS3b3nXNeuXIspKd84AIAqaWlpOa
|
||||
I16NhJsU8VOq6I5RCgkx4WgmmUhCmzjLbYDH7rjj1dehCZa0Y63mlMdTKKs4BJSk
|
||||
AtSVVV6nTupZdHPJtpQ1RxcT6iTy8Nr13cVuKnluui7KZ/uktOdB0H1o5kuWchvm
|
||||
8/oqLVSfoqjhU6Fn/11Af+iCnpICUw==
|
||||
=QRnC
|
||||
-----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.27.0.dev0"
|
||||
LE_AUTO_VERSION="0.28.0.dev0"
|
||||
BASENAME=$(basename $0)
|
||||
USAGE="Usage: $BASENAME [OPTIONS]
|
||||
A self-updating wrapper script for the Certbot ACME client. When run, updates
|
||||
|
|
@ -195,7 +195,7 @@ if [ "$1" = "--cb-auto-has-root" ]; then
|
|||
else
|
||||
SetRootAuthMechanism
|
||||
if [ -n "$SUDO" ]; then
|
||||
echo "Requesting to rerun $0 with root privileges..."
|
||||
say "Requesting to rerun $0 with root privileges..."
|
||||
$SUDO "$0" --cb-auto-has-root "$@"
|
||||
exit 0
|
||||
fi
|
||||
|
|
@ -1197,18 +1197,18 @@ letsencrypt==0.7.0 \
|
|||
--hash=sha256:105a5fb107e45bcd0722eb89696986dcf5f08a86a321d6aef25a0c7c63375ade \
|
||||
--hash=sha256:c36e532c486a7e92155ee09da54b436a3c420813ec1c590b98f635d924720de9
|
||||
|
||||
certbot==0.26.1 \
|
||||
--hash=sha256:4e2ffdeebb7f5097600bcb1ca19131441fa021f952b443ca7454a279337af609 \
|
||||
--hash=sha256:4983513d63f7f36e24a07873ca2d6ea1c0101aa6cb1cd825cda02ed520f6ca66
|
||||
acme==0.26.1 \
|
||||
--hash=sha256:d47841e66adc1336ecca2f0d41a247c1b62307c981be6d07996bbf3f95af1dc5 \
|
||||
--hash=sha256:86e7b5f4654cb19215f16c0e6225750db7421f68ef6a0a040a61796f24e690be
|
||||
certbot-apache==0.26.1 \
|
||||
--hash=sha256:c16acb49bd4f84fff25bcbb7eaf74412145efe9b68ce46e1803be538894f2ce3 \
|
||||
--hash=sha256:b7fa327e987b892d64163e7519bdeaf9723d78275ef6c438272848894ace6d87
|
||||
certbot-nginx==0.26.1 \
|
||||
--hash=sha256:c0048dc83672dc90805a8ddf513be3e48c841d6e91607e91e8657c1785d65660 \
|
||||
--hash=sha256:d0c95a32625e0f1612d7fcf9021e6e050ba3d879823489d1edd2478a78ae6624
|
||||
certbot==0.27.1 \
|
||||
--hash=sha256:89a8d8e44e272ee970259c93fa2ff2c9f063da8fd88a56d7ca30d7a2218791ea \
|
||||
--hash=sha256:3570bd14ed223c752f309dbd082044bd9f11a339d21671e70a2eeae4e51ed02a
|
||||
acme==0.27.1 \
|
||||
--hash=sha256:0d42cfc9050a2e1d6d4e6b66334df8173778db0b3fe7a2b3bcb58f7034913597 \
|
||||
--hash=sha256:31a7b9023ce183616e6ebd5d783e842c3d68696ff70db59a06db9feea8f54f90
|
||||
certbot-apache==0.27.1 \
|
||||
--hash=sha256:1c73297e6a59cebcf5f5692025d4013ccd02c858bdc946fee3c6613f62bb9414 \
|
||||
--hash=sha256:61d6d706d49d726b53a831a2ea9099bd6c02657ff537a166dd197cd5f494d854
|
||||
certbot-nginx==0.27.1 \
|
||||
--hash=sha256:9772198bcfde9b68e448c15c3801b3cf9d20eb9ea9da1d9f4f9a7692b0fc2314 \
|
||||
--hash=sha256:ff5b849a9b4e3d1fd50ea351a1393738382fc9bd47bc5ac18c343d11a691349f
|
||||
|
||||
UNLIKELY_EOF
|
||||
# -------------------------------------------------------------------------
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -195,7 +195,7 @@ if [ "$1" = "--cb-auto-has-root" ]; then
|
|||
else
|
||||
SetRootAuthMechanism
|
||||
if [ -n "$SUDO" ]; then
|
||||
echo "Requesting to rerun $0 with root privileges..."
|
||||
say "Requesting to rerun $0 with root privileges..."
|
||||
$SUDO "$0" --cb-auto-has-root "$@"
|
||||
exit 0
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
certbot==0.26.1 \
|
||||
--hash=sha256:4e2ffdeebb7f5097600bcb1ca19131441fa021f952b443ca7454a279337af609 \
|
||||
--hash=sha256:4983513d63f7f36e24a07873ca2d6ea1c0101aa6cb1cd825cda02ed520f6ca66
|
||||
acme==0.26.1 \
|
||||
--hash=sha256:d47841e66adc1336ecca2f0d41a247c1b62307c981be6d07996bbf3f95af1dc5 \
|
||||
--hash=sha256:86e7b5f4654cb19215f16c0e6225750db7421f68ef6a0a040a61796f24e690be
|
||||
certbot-apache==0.26.1 \
|
||||
--hash=sha256:c16acb49bd4f84fff25bcbb7eaf74412145efe9b68ce46e1803be538894f2ce3 \
|
||||
--hash=sha256:b7fa327e987b892d64163e7519bdeaf9723d78275ef6c438272848894ace6d87
|
||||
certbot-nginx==0.26.1 \
|
||||
--hash=sha256:c0048dc83672dc90805a8ddf513be3e48c841d6e91607e91e8657c1785d65660 \
|
||||
--hash=sha256:d0c95a32625e0f1612d7fcf9021e6e050ba3d879823489d1edd2478a78ae6624
|
||||
certbot==0.27.1 \
|
||||
--hash=sha256:89a8d8e44e272ee970259c93fa2ff2c9f063da8fd88a56d7ca30d7a2218791ea \
|
||||
--hash=sha256:3570bd14ed223c752f309dbd082044bd9f11a339d21671e70a2eeae4e51ed02a
|
||||
acme==0.27.1 \
|
||||
--hash=sha256:0d42cfc9050a2e1d6d4e6b66334df8173778db0b3fe7a2b3bcb58f7034913597 \
|
||||
--hash=sha256:31a7b9023ce183616e6ebd5d783e842c3d68696ff70db59a06db9feea8f54f90
|
||||
certbot-apache==0.27.1 \
|
||||
--hash=sha256:1c73297e6a59cebcf5f5692025d4013ccd02c858bdc946fee3c6613f62bb9414 \
|
||||
--hash=sha256:61d6d706d49d726b53a831a2ea9099bd6c02657ff537a166dd197cd5f494d854
|
||||
certbot-nginx==0.27.1 \
|
||||
--hash=sha256:9772198bcfde9b68e448c15c3801b3cf9d20eb9ea9da1d9f4f9a7692b0fc2314 \
|
||||
--hash=sha256:ff5b849a9b4e3d1fd50ea351a1393738382fc9bd47bc5ac18c343d11a691349f
|
||||
|
|
|
|||
1
pull_request_template.md
Normal file
1
pull_request_template.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Be sure to edit the `master` section of `CHANGELOG.md` with a line describing this PR before it gets merged.
|
||||
3
setup.py
3
setup.py
|
|
@ -25,7 +25,6 @@ init_fn = os.path.join(here, 'certbot', '__init__.py')
|
|||
meta = dict(re.findall(r"""__([a-z]+)__ = '([^']+)""", read_file(init_fn)))
|
||||
|
||||
readme = read_file(os.path.join(here, 'README.rst'))
|
||||
changes = read_file(os.path.join(here, 'CHANGES.rst'))
|
||||
version = meta['version']
|
||||
|
||||
# This package relies on PyOpenSSL, requests, and six, however, it isn't
|
||||
|
|
@ -79,7 +78,7 @@ setup(
|
|||
name='certbot',
|
||||
version=version,
|
||||
description="ACME client",
|
||||
long_description=readme, # later: + '\n\n' + changes
|
||||
long_description=readme,
|
||||
url='https://github.com/letsencrypt/letsencrypt',
|
||||
author="Certbot Project",
|
||||
author_email='client-dev@letsencrypt.org',
|
||||
|
|
|
|||
|
|
@ -185,10 +185,10 @@ get_num_tmp_files() {
|
|||
ls -1 /tmp | wc -l
|
||||
}
|
||||
num_tmp_files=$(get_num_tmp_files)
|
||||
common --csr / && echo expected error && exit 1 || true
|
||||
common --help
|
||||
common --help all
|
||||
common --version
|
||||
common --csr / > /dev/null && echo expected error && exit 1 || true
|
||||
common --help > /dev/null
|
||||
common --help all > /dev/null
|
||||
common --version > /dev/null
|
||||
if [ $(get_num_tmp_files) -ne $num_tmp_files ]; then
|
||||
echo "New files or directories created in /tmp!"
|
||||
exit 1
|
||||
|
|
@ -440,25 +440,15 @@ for subdomain in $subdomains; do
|
|||
fi
|
||||
done
|
||||
|
||||
# Test that revocation raises correct error if --cert-name and --cert-path don't match
|
||||
# Test that revocation raises correct error when both --cert-name and --cert-path specified
|
||||
common --domains le1.wtf
|
||||
common --domains le2.wtf
|
||||
out=$(common revoke --cert-path "$root/conf/live/le1.wtf/fullchain.pem" --cert-name "le2.wtf" 2>&1) || true
|
||||
if ! echo $out | grep "or both must point to the same certificate lineages."; then
|
||||
echo "Non-interactive revoking with mismatched --cert-name and --cert-path "
|
||||
out=$(common revoke --cert-path "$root/conf/live/le1.wtf/fullchain.pem" --cert-name "le1.wtf" 2>&1) || true
|
||||
if ! echo $out | grep "Exactly one of --cert-path or --cert-name must be specified"; then
|
||||
echo "Non-interactive revoking with both --cert-name and --cert-path "
|
||||
echo "did not raise the correct error!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Revoking by matching --cert-name and --cert-path deletes
|
||||
common --domains le1.wtf
|
||||
common revoke --cert-path "$root/conf/live/le1.wtf/fullchain.pem" --cert-name "le1.wtf"
|
||||
out=$(common certificates)
|
||||
if echo $out | grep "le1.wtf"; then
|
||||
echo "Cert le1.wtf should've been deleted! Was revoked via matching --cert-path & --cert-name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test that revocation doesn't delete if multiple lineages share an archive dir
|
||||
common --domains le1.wtf
|
||||
common --domains le2.wtf
|
||||
|
|
|
|||
|
|
@ -103,13 +103,32 @@ LOGDIR = "" #points to logging / working directory
|
|||
# boto3/AWS api globals
|
||||
AWS_SESSION = None
|
||||
EC2 = None
|
||||
SECURITY_GROUP_NAME = 'certbot-security-group'
|
||||
SUBNET_NAME = 'certbot-subnet'
|
||||
|
||||
# Boto3/AWS automation functions
|
||||
#-------------------------------------------------------------------------------
|
||||
def make_security_group():
|
||||
def should_use_subnet(subnet):
|
||||
"""Should we use the given subnet for these tests?
|
||||
|
||||
We should if it is the default subnet for the availability zone or the
|
||||
subnet is named "certbot-subnet".
|
||||
|
||||
"""
|
||||
if not subnet.map_public_ip_on_launch:
|
||||
return False
|
||||
if subnet.default_for_az:
|
||||
return True
|
||||
for tag in subnet.tags:
|
||||
if tag['Key'] == 'Name' and tag['Value'] == SUBNET_NAME:
|
||||
return True
|
||||
return False
|
||||
|
||||
def make_security_group(vpc):
|
||||
"""Creates a security group in the given VPC."""
|
||||
# will fail if security group of GroupName already exists
|
||||
# cannot have duplicate SGs of the same name
|
||||
mysg = EC2.create_security_group(GroupName="letsencrypt_test",
|
||||
mysg = vpc.create_security_group(GroupName=SECURITY_GROUP_NAME,
|
||||
Description='security group for automated testing')
|
||||
mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=22, ToPort=22)
|
||||
mysg.authorize_ingress(IpProtocol="tcp", CidrIp="0.0.0.0/0", FromPort=80, ToPort=80)
|
||||
|
|
@ -123,14 +142,16 @@ def make_security_group():
|
|||
def make_instance(instance_name,
|
||||
ami_id,
|
||||
keyname,
|
||||
security_group_id,
|
||||
subnet_id,
|
||||
machine_type='t2.micro',
|
||||
security_groups=['letsencrypt_test'],
|
||||
userdata=""): #userdata contains bash or cloud-init script
|
||||
|
||||
new_instance = EC2.create_instances(
|
||||
BlockDeviceMappings=_get_block_device_mappings(ami_id),
|
||||
ImageId=ami_id,
|
||||
SecurityGroups=security_groups,
|
||||
SecurityGroupIds=[security_group_id],
|
||||
SubnetId=subnet_id,
|
||||
KeyName=keyname,
|
||||
MinCount=1,
|
||||
MaxCount=1,
|
||||
|
|
@ -294,7 +315,7 @@ def grab_certbot_log():
|
|||
sudo('if [ -f ./certbot.log ]; then \
|
||||
cat ./certbot.log; else echo "[nolocallog]"; fi')
|
||||
|
||||
def create_client_instances(targetlist):
|
||||
def create_client_instances(targetlist, security_group_id, subnet_id):
|
||||
"Create a fleet of client instances"
|
||||
instances = []
|
||||
print("Creating instances: ", end="")
|
||||
|
|
@ -314,6 +335,8 @@ def create_client_instances(targetlist):
|
|||
target['ami'],
|
||||
KEYNAME,
|
||||
machine_type=machine_type,
|
||||
security_group_id=security_group_id,
|
||||
subnet_id=subnet_id,
|
||||
userdata=userdata))
|
||||
print()
|
||||
return instances
|
||||
|
|
@ -418,14 +441,28 @@ print("Connecting to EC2 using\n profile %s\n keyname %s\n keyfile %s"%(PROFILE,
|
|||
AWS_SESSION = boto3.session.Session(profile_name=PROFILE)
|
||||
EC2 = AWS_SESSION.resource('ec2')
|
||||
|
||||
print("Determining Subnet")
|
||||
for subnet in EC2.subnets.all():
|
||||
if should_use_subnet(subnet):
|
||||
subnet_id = subnet.id
|
||||
vpc_id = subnet.vpc.id
|
||||
break
|
||||
else:
|
||||
print("No usable subnet exists!")
|
||||
print("Please create a VPC with a subnet named {0}".format(SUBNET_NAME))
|
||||
print("that maps public IPv4 addresses to instances launched in the subnet.")
|
||||
sys.exit(1)
|
||||
|
||||
print("Making Security Group")
|
||||
vpc = EC2.Vpc(vpc_id)
|
||||
sg_exists = False
|
||||
for sg in EC2.security_groups.all():
|
||||
if sg.group_name == 'letsencrypt_test':
|
||||
for sg in vpc.security_groups.all():
|
||||
if sg.group_name == SECURITY_GROUP_NAME:
|
||||
security_group_id = sg.id
|
||||
sg_exists = True
|
||||
print(" %s already exists"%'letsencrypt_test')
|
||||
print(" %s already exists"%SECURITY_GROUP_NAME)
|
||||
if not sg_exists:
|
||||
make_security_group()
|
||||
security_group_id = make_security_group(vpc).id
|
||||
time.sleep(30)
|
||||
|
||||
boulder_preexists = False
|
||||
|
|
@ -446,11 +483,12 @@ else:
|
|||
KEYNAME,
|
||||
machine_type='t2.micro',
|
||||
#machine_type='t2.medium',
|
||||
security_groups=['letsencrypt_test'])
|
||||
security_group_id=security_group_id,
|
||||
subnet_id=subnet_id)
|
||||
|
||||
try:
|
||||
if not cl_args.boulderonly:
|
||||
instances = create_client_instances(targetlist)
|
||||
instances = create_client_instances(targetlist, security_group_id, subnet_id)
|
||||
|
||||
# Configure and launch boulder server
|
||||
#-------------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -48,13 +48,13 @@ targets:
|
|||
# CentOS
|
||||
# These Marketplace AMIs must, irritatingly, have their terms manually
|
||||
# agreed to on the AWS marketplace site for any new AWS account using them...
|
||||
- ami: ami-61bbf104
|
||||
- ami: ami-9887c6e7
|
||||
name: centos7
|
||||
type: centos
|
||||
virt: hvm
|
||||
user: centos
|
||||
# centos6 requires EPEL repo added
|
||||
- ami: ami-57cd8732
|
||||
- ami: ami-1585c46a
|
||||
name: centos6
|
||||
type: centos
|
||||
virt: hvm
|
||||
|
|
|
|||
249
tools/_release.sh
Executable file
249
tools/_release.sh
Executable file
|
|
@ -0,0 +1,249 @@
|
|||
#!/bin/bash -xe
|
||||
# Release packages to PyPI
|
||||
|
||||
if [ "$RELEASE_DIR" = "" ]; then
|
||||
echo Please run this script through the tools/release.sh wrapper script or set the environment
|
||||
echo variable RELEASE_DIR to the directory where the release should be built.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="$1"
|
||||
echo Releasing production version "$version"...
|
||||
nextversion="$2"
|
||||
RELEASE_BRANCH="candidate-$version"
|
||||
|
||||
if [ "$RELEASE_OPENSSL_PUBKEY" = "" ] ; then
|
||||
RELEASE_OPENSSL_PUBKEY="`realpath \`dirname $0\``/eff-pubkey.pem"
|
||||
fi
|
||||
RELEASE_GPG_KEY=${RELEASE_GPG_KEY:-A2CFB51FA275A7286234E7B24D17C995CD9775F2}
|
||||
# Needed to fix problems with git signatures and pinentry
|
||||
export GPG_TTY=$(tty)
|
||||
|
||||
# port for a local Python Package Index (used in testing)
|
||||
PORT=${PORT:-1234}
|
||||
|
||||
# subpackages to be released (the way developers think about them)
|
||||
SUBPKGS_IN_AUTO_NO_CERTBOT="acme certbot-apache certbot-nginx"
|
||||
SUBPKGS_NOT_IN_AUTO="certbot-dns-cloudflare certbot-dns-cloudxns certbot-dns-digitalocean certbot-dns-dnsimple certbot-dns-dnsmadeeasy certbot-dns-gehirn certbot-dns-google certbot-dns-linode certbot-dns-luadns certbot-dns-nsone certbot-dns-ovh certbot-dns-rfc2136 certbot-dns-route53 certbot-dns-sakuracloud"
|
||||
|
||||
# subpackages to be released (the way the script thinks about them)
|
||||
SUBPKGS_IN_AUTO="certbot $SUBPKGS_IN_AUTO_NO_CERTBOT"
|
||||
SUBPKGS_NO_CERTBOT="$SUBPKGS_IN_AUTO_NO_CERTBOT $SUBPKGS_NOT_IN_AUTO"
|
||||
SUBPKGS="$SUBPKGS_IN_AUTO $SUBPKGS_NOT_IN_AUTO"
|
||||
subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)"
|
||||
# certbot_compatibility_test is not packaged because:
|
||||
# - it is not meant to be used by anyone else than Certbot devs
|
||||
# - it causes problems when running pytest - the latter tries to
|
||||
# run everything that matches test*, while there are no unittests
|
||||
# there
|
||||
|
||||
tag="v$version"
|
||||
mv "dist.$version" "dist.$version.$(date +%s).bak" || true
|
||||
git tag --delete "$tag" || true
|
||||
|
||||
tmpvenv=$(mktemp -d)
|
||||
virtualenv --no-site-packages -p python2 $tmpvenv
|
||||
. $tmpvenv/bin/activate
|
||||
# update setuptools/pip just like in other places in the repo
|
||||
pip install -U setuptools
|
||||
pip install -U pip # latest pip => no --pre for dev releases
|
||||
pip install -U wheel # setup.py bdist_wheel
|
||||
|
||||
# newer versions of virtualenv inherit setuptools/pip/wheel versions
|
||||
# from current env when creating a child env
|
||||
pip install -U virtualenv
|
||||
|
||||
root_without_le="$version.$$"
|
||||
root="$RELEASE_DIR/le.$root_without_le"
|
||||
|
||||
echo "Cloning into fresh copy at $root" # clean repo = no artifacts
|
||||
git clone . $root
|
||||
git rev-parse HEAD
|
||||
cd $root
|
||||
if [ "$RELEASE_BRANCH" != "candidate-$version" ] ; then
|
||||
git branch -f "$RELEASE_BRANCH"
|
||||
fi
|
||||
git checkout "$RELEASE_BRANCH"
|
||||
|
||||
for pkg_dir in $SUBPKGS_NO_CERTBOT certbot-compatibility-test .
|
||||
do
|
||||
sed -i 's/\.dev0//' "$pkg_dir/setup.py"
|
||||
git add "$pkg_dir/setup.py"
|
||||
done
|
||||
|
||||
SetVersion() {
|
||||
ver="$1"
|
||||
# bumping Certbot's version number is done differently
|
||||
for pkg_dir in $SUBPKGS_NO_CERTBOT certbot-compatibility-test
|
||||
do
|
||||
setup_file="$pkg_dir/setup.py"
|
||||
if [ $(grep -c '^version' "$setup_file") != 1 ]; then
|
||||
echo "Unexpected count of version variables in $setup_file"
|
||||
exit 1
|
||||
fi
|
||||
sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py
|
||||
done
|
||||
init_file="certbot/__init__.py"
|
||||
if [ $(grep -c '^__version' "$init_file") != 1 ]; then
|
||||
echo "Unexpected count of __version variables in $init_file"
|
||||
exit 1
|
||||
fi
|
||||
sed -i "s/^__version.*/__version__ = '$ver'/" "$init_file"
|
||||
|
||||
git add $SUBPKGS certbot-compatibility-test
|
||||
}
|
||||
|
||||
SetVersion "$version"
|
||||
|
||||
echo "Preparing sdists and wheels"
|
||||
for pkg_dir in . $SUBPKGS_NO_CERTBOT
|
||||
do
|
||||
cd $pkg_dir
|
||||
|
||||
python setup.py clean
|
||||
rm -rf build dist
|
||||
python setup.py sdist
|
||||
python setup.py bdist_wheel
|
||||
|
||||
echo "Signing ($pkg_dir)"
|
||||
for x in dist/*.tar.gz dist/*.whl
|
||||
do
|
||||
gpg2 -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign --digest-algo sha256 $x
|
||||
done
|
||||
|
||||
cd -
|
||||
done
|
||||
|
||||
|
||||
mkdir "dist.$version"
|
||||
mv dist "dist.$version/certbot"
|
||||
for pkg_dir in $SUBPKGS_NO_CERTBOT
|
||||
do
|
||||
mv $pkg_dir/dist "dist.$version/$pkg_dir/"
|
||||
done
|
||||
|
||||
echo "Testing packages"
|
||||
cd "dist.$version"
|
||||
# start local PyPI
|
||||
python -m SimpleHTTPServer $PORT &
|
||||
# cd .. is NOT done on purpose: we make sure that all subpackages are
|
||||
# installed from local PyPI rather than current directory (repo root)
|
||||
virtualenv --no-site-packages ../venv
|
||||
. ../venv/bin/activate
|
||||
pip install -U setuptools
|
||||
pip install -U pip
|
||||
# Now, use our local PyPI. Disable cache so we get the correct KGS even if we
|
||||
# (or our dependencies) have conditional dependencies implemented with if
|
||||
# statements in setup.py and we have cached wheels lying around that would
|
||||
# cause those ifs to not be evaluated.
|
||||
pip install \
|
||||
--no-cache-dir \
|
||||
--extra-index-url http://localhost:$PORT \
|
||||
$SUBPKGS
|
||||
# stop local PyPI
|
||||
kill $!
|
||||
cd ~-
|
||||
|
||||
# get a snapshot of the CLI help for the docs
|
||||
# We set CERTBOT_DOCS to use dummy values in example user-agent string.
|
||||
CERTBOT_DOCS=1 certbot --help all > docs/cli-help.txt
|
||||
jws --help > acme/docs/jws-help.txt
|
||||
|
||||
cd ..
|
||||
# freeze before installing anything else, so that we know end-user KGS
|
||||
# make sure "twine upload" doesn't catch "kgs"
|
||||
if [ -d kgs ] ; then
|
||||
echo Deleting old kgs...
|
||||
rm -rf kgs
|
||||
fi
|
||||
mkdir kgs
|
||||
kgs="kgs/$version"
|
||||
pip freeze | tee $kgs
|
||||
pip install pytest
|
||||
for module in $subpkgs_modules ; do
|
||||
echo testing $module
|
||||
pytest --pyargs $module
|
||||
done
|
||||
cd ~-
|
||||
|
||||
# pin pip hashes of the things we just built
|
||||
for pkg in $SUBPKGS_IN_AUTO ; do
|
||||
echo $pkg==$version \\
|
||||
pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),'
|
||||
done > letsencrypt-auto-source/pieces/certbot-requirements.txt
|
||||
deactivate
|
||||
|
||||
# there should be one requirement specifier and two hashes for each subpackage
|
||||
expected_count=$(expr $(echo $SUBPKGS_IN_AUTO | wc -w) \* 3)
|
||||
if ! wc -l letsencrypt-auto-source/pieces/certbot-requirements.txt | grep -qE "^\s*$expected_count " ; then
|
||||
echo Unexpected pip hash output
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ensure we have the latest built version of leauto
|
||||
letsencrypt-auto-source/build.py
|
||||
|
||||
# and that it's signed correctly
|
||||
tools/offline-sigrequest.sh || true
|
||||
while ! openssl dgst -sha256 -verify $RELEASE_OPENSSL_PUBKEY -signature \
|
||||
letsencrypt-auto-source/letsencrypt-auto.sig \
|
||||
letsencrypt-auto-source/letsencrypt-auto ; do
|
||||
echo "The signature on letsencrypt-auto is not correct."
|
||||
read -p "Would you like this script to try and sign it again [Y/n]?" response
|
||||
case $response in
|
||||
[yY][eE][sS]|[yY]|"")
|
||||
tools/offline-sigrequest.sh || true;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This signature is not quite as strong, but easier for people to verify out of band
|
||||
while ! gpg2 -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign --digest-algo sha256 letsencrypt-auto-source/letsencrypt-auto; do
|
||||
echo "Unable to sign letsencrypt-auto using $RELEASE_KEY."
|
||||
echo "Make sure your OpenPGP card is in your computer if you are using one."
|
||||
echo "You may need to take the card out and put it back in again."
|
||||
read -p "Press enter to try signing again."
|
||||
done
|
||||
# We can't rename the openssl letsencrypt-auto.sig for compatibility reasons,
|
||||
# but we can use the right name for certbot-auto.asc from day one
|
||||
mv letsencrypt-auto-source/letsencrypt-auto.asc letsencrypt-auto-source/certbot-auto.asc
|
||||
|
||||
# copy leauto to the root, overwriting the previous release version
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto
|
||||
|
||||
git add certbot-auto letsencrypt-auto letsencrypt-auto-source docs/cli-help.txt
|
||||
git diff --cached
|
||||
while ! git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version"; do
|
||||
echo "Unable to sign the release commit using git."
|
||||
echo "You may have to configure git to use gpg2 by running:"
|
||||
echo 'git config --global gpg.program $(command -v gpg2)'
|
||||
read -p "Press enter to try signing again."
|
||||
done
|
||||
git tag --local-user "$RELEASE_GPG_KEY" --sign --message "Release $version" "$tag"
|
||||
|
||||
cd ..
|
||||
echo Now in $PWD
|
||||
name=${root_without_le%.*}
|
||||
ext="${root_without_le##*.}"
|
||||
rev="$(git rev-parse --short HEAD)"
|
||||
echo tar cJvf $name.$rev.tar.xz $name.$rev
|
||||
echo gpg2 -U $RELEASE_GPG_KEY --detach-sign --armor $name.$rev.tar.xz
|
||||
cd ~-
|
||||
|
||||
echo "New root: $root"
|
||||
echo "Test commands (in the letstest repo):"
|
||||
echo 'python multitester.py targets.yaml $AWS_KEY $USERNAME scripts/test_leauto_upgrades.sh --alt_pip $YOUR_PIP_REPO --branch public-beta'
|
||||
echo 'python multitester.py targets.yaml $AWK_KEY $USERNAME scripts/test_letsencrypt_auto_certonly_standalone.sh --branch candidate-0.1.1'
|
||||
echo 'python multitester.py --saveinstances targets.yaml $AWS_KEY $USERNAME scripts/test_apache2.sh'
|
||||
echo "In order to upload packages run the following command:"
|
||||
echo twine upload "$root/dist.$version/*/*"
|
||||
|
||||
if [ "$RELEASE_BRANCH" = candidate-"$version" ] ; then
|
||||
SetVersion "$nextversion".dev0
|
||||
letsencrypt-auto-source/build.py
|
||||
git add letsencrypt-auto-source/letsencrypt-auto
|
||||
git diff
|
||||
git commit -m "Bump version to $nextversion"
|
||||
fi
|
||||
|
|
@ -12,7 +12,7 @@ botocore==1.7.41
|
|||
cloudflare==1.5.1
|
||||
coverage==4.4.2
|
||||
decorator==4.1.2
|
||||
dns-lexicon==2.2.1
|
||||
dns-lexicon==2.7.3
|
||||
dnspython==1.15.0
|
||||
docutils==0.14
|
||||
execnet==1.5.0
|
||||
|
|
|
|||
257
tools/release.sh
257
tools/release.sh
|
|
@ -1,11 +1,5 @@
|
|||
#!/bin/bash -xe
|
||||
# Release dev packages to PyPI
|
||||
|
||||
Usage() {
|
||||
echo Usage:
|
||||
echo "$0 [ --production ]"
|
||||
exit 1
|
||||
}
|
||||
#!/bin/bash -e
|
||||
# Release packages to PyPI
|
||||
|
||||
if [ "`dirname $0`" != "tools" ] ; then
|
||||
echo Please run this script from the repo root
|
||||
|
|
@ -13,235 +7,38 @@ if [ "`dirname $0`" != "tools" ] ; then
|
|||
fi
|
||||
|
||||
CheckVersion() {
|
||||
# Args: <description of version type> <version number>
|
||||
if ! echo "$2" | grep -q -e '[0-9]\+.[0-9]\+.[0-9]\+' ; then
|
||||
# Args: <version number>
|
||||
if ! echo "$1" | grep -q -e '[0-9]\+.[0-9]\+.[0-9]\+' ; then
|
||||
echo "$1 doesn't look like 1.2.3"
|
||||
echo "Usage:"
|
||||
echo "$0 RELEASE_VERSION NEXT_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$1" = "--production" ] ; then
|
||||
version="$2"
|
||||
CheckVersion Version "$version"
|
||||
echo Releasing production version "$version"...
|
||||
nextversion="$3"
|
||||
CheckVersion "Next version" "$nextversion"
|
||||
RELEASE_BRANCH="candidate-$version"
|
||||
else
|
||||
version=`grep "__version__" certbot/__init__.py | cut -d\' -f2 | sed s/\.dev0//`
|
||||
version="$version.dev$(date +%Y%m%d)1"
|
||||
RELEASE_BRANCH="dev-release"
|
||||
echo Releasing developer version "$version"...
|
||||
fi
|
||||
CheckVersion "$1"
|
||||
CheckVersion "$2"
|
||||
|
||||
if [ "$RELEASE_OPENSSL_PUBKEY" = "" ] ; then
|
||||
RELEASE_OPENSSL_PUBKEY="`realpath \`dirname $0\``/eff-pubkey.pem"
|
||||
fi
|
||||
RELEASE_GPG_KEY=${RELEASE_GPG_KEY:-A2CFB51FA275A7286234E7B24D17C995CD9775F2}
|
||||
# Needed to fix problems with git signatures and pinentry
|
||||
export GPG_TTY=$(tty)
|
||||
|
||||
# port for a local Python Package Index (used in testing)
|
||||
PORT=${PORT:-1234}
|
||||
|
||||
# subpackages to be released (the way developers think about them)
|
||||
SUBPKGS_IN_AUTO_NO_CERTBOT="acme certbot-apache certbot-nginx"
|
||||
SUBPKGS_NOT_IN_AUTO="certbot-dns-cloudflare certbot-dns-cloudxns certbot-dns-digitalocean certbot-dns-dnsimple certbot-dns-dnsmadeeasy certbot-dns-gehirn certbot-dns-google certbot-dns-linode certbot-dns-luadns certbot-dns-nsone certbot-dns-ovh certbot-dns-rfc2136 certbot-dns-route53 certbot-dns-sakuracloud"
|
||||
|
||||
# subpackages to be released (the way the script thinks about them)
|
||||
SUBPKGS_IN_AUTO="certbot $SUBPKGS_IN_AUTO_NO_CERTBOT"
|
||||
SUBPKGS_NO_CERTBOT="$SUBPKGS_IN_AUTO_NO_CERTBOT $SUBPKGS_NOT_IN_AUTO"
|
||||
SUBPKGS="$SUBPKGS_IN_AUTO $SUBPKGS_NOT_IN_AUTO"
|
||||
subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)"
|
||||
# certbot_compatibility_test is not packaged because:
|
||||
# - it is not meant to be used by anyone else than Certbot devs
|
||||
# - it causes problems when running pytest - the latter tries to
|
||||
# run everything that matches test*, while there are no unittests
|
||||
# there
|
||||
|
||||
tag="v$version"
|
||||
mv "dist.$version" "dist.$version.$(date +%s).bak" || true
|
||||
git tag --delete "$tag" || true
|
||||
|
||||
tmpvenv=$(mktemp -d)
|
||||
virtualenv --no-site-packages -p python2 $tmpvenv
|
||||
. $tmpvenv/bin/activate
|
||||
# update setuptools/pip just like in other places in the repo
|
||||
pip install -U setuptools
|
||||
pip install -U pip # latest pip => no --pre for dev releases
|
||||
pip install -U wheel # setup.py bdist_wheel
|
||||
|
||||
# newer versions of virtualenv inherit setuptools/pip/wheel versions
|
||||
# from current env when creating a child env
|
||||
pip install -U virtualenv
|
||||
|
||||
root_without_le="$version.$$"
|
||||
root="./releases/le.$root_without_le"
|
||||
|
||||
echo "Cloning into fresh copy at $root" # clean repo = no artifacts
|
||||
git clone . $root
|
||||
git rev-parse HEAD
|
||||
cd $root
|
||||
if [ "$RELEASE_BRANCH" != "candidate-$version" ] ; then
|
||||
git branch -f "$RELEASE_BRANCH"
|
||||
fi
|
||||
git checkout "$RELEASE_BRANCH"
|
||||
|
||||
for pkg_dir in $SUBPKGS_NO_CERTBOT certbot-compatibility-test .
|
||||
do
|
||||
sed -i 's/\.dev0//' "$pkg_dir/setup.py"
|
||||
done
|
||||
# We only add Certbot's setup.py here because the other files are added in the
|
||||
# call to SetVersion below.
|
||||
git add -p setup.py
|
||||
|
||||
SetVersion() {
|
||||
ver="$1"
|
||||
# bumping Certbot's version number is done differently
|
||||
for pkg_dir in $SUBPKGS_NO_CERTBOT certbot-compatibility-test
|
||||
do
|
||||
sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py
|
||||
done
|
||||
sed -i "s/^__version.*/__version__ = '$ver'/" certbot/__init__.py
|
||||
|
||||
# interactive user input
|
||||
git add -p $SUBPKGS certbot-compatibility-test
|
||||
|
||||
}
|
||||
|
||||
SetVersion "$version"
|
||||
|
||||
echo "Preparing sdists and wheels"
|
||||
for pkg_dir in . $SUBPKGS_NO_CERTBOT
|
||||
do
|
||||
cd $pkg_dir
|
||||
|
||||
python setup.py clean
|
||||
rm -rf build dist
|
||||
python setup.py sdist
|
||||
python setup.py bdist_wheel
|
||||
|
||||
echo "Signing ($pkg_dir)"
|
||||
for x in dist/*.tar.gz dist/*.whl
|
||||
do
|
||||
gpg2 -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign --digest-algo sha256 $x
|
||||
done
|
||||
|
||||
cd -
|
||||
done
|
||||
|
||||
|
||||
mkdir "dist.$version"
|
||||
mv dist "dist.$version/certbot"
|
||||
for pkg_dir in $SUBPKGS_NO_CERTBOT
|
||||
do
|
||||
mv $pkg_dir/dist "dist.$version/$pkg_dir/"
|
||||
done
|
||||
|
||||
echo "Testing packages"
|
||||
cd "dist.$version"
|
||||
# start local PyPI
|
||||
python -m SimpleHTTPServer $PORT &
|
||||
# cd .. is NOT done on purpose: we make sure that all subpackages are
|
||||
# installed from local PyPI rather than current directory (repo root)
|
||||
virtualenv --no-site-packages ../venv
|
||||
. ../venv/bin/activate
|
||||
pip install -U setuptools
|
||||
pip install -U pip
|
||||
# Now, use our local PyPI. Disable cache so we get the correct KGS even if we
|
||||
# (or our dependencies) have conditional dependencies implemented with if
|
||||
# statements in setup.py and we have cached wheels lying around that would
|
||||
# cause those ifs to not be evaluated.
|
||||
pip install \
|
||||
--no-cache-dir \
|
||||
--extra-index-url http://localhost:$PORT \
|
||||
$SUBPKGS
|
||||
# stop local PyPI
|
||||
kill $!
|
||||
cd ~-
|
||||
|
||||
# get a snapshot of the CLI help for the docs
|
||||
# We set CERTBOT_DOCS to use dummy values in example user-agent string.
|
||||
CERTBOT_DOCS=1 certbot --help all > docs/cli-help.txt
|
||||
jws --help > acme/docs/jws-help.txt
|
||||
|
||||
cd ..
|
||||
# freeze before installing anything else, so that we know end-user KGS
|
||||
# make sure "twine upload" doesn't catch "kgs"
|
||||
if [ -d kgs ] ; then
|
||||
echo Deleting old kgs...
|
||||
rm -rf kgs
|
||||
fi
|
||||
mkdir kgs
|
||||
kgs="kgs/$version"
|
||||
pip freeze | tee $kgs
|
||||
pip install pytest
|
||||
for module in $subpkgs_modules ; do
|
||||
echo testing $module
|
||||
pytest --pyargs $module
|
||||
done
|
||||
cd ~-
|
||||
|
||||
# pin pip hashes of the things we just built
|
||||
for pkg in $SUBPKGS_IN_AUTO ; do
|
||||
echo $pkg==$version \\
|
||||
pip hash dist."$version/$pkg"/*.{whl,gz} | grep "^--hash" | python2 -c 'from sys import stdin; input = stdin.read(); print " ", input.replace("\n--hash", " \\\n --hash"),'
|
||||
done > letsencrypt-auto-source/pieces/certbot-requirements.txt
|
||||
deactivate
|
||||
|
||||
# there should be one requirement specifier and two hashes for each subpackage
|
||||
expected_count=$(expr $(echo $SUBPKGS_IN_AUTO | wc -w) \* 3)
|
||||
if ! wc -l letsencrypt-auto-source/pieces/certbot-requirements.txt | grep -qE "^\s*$expected_count " ; then
|
||||
echo Unexpected pip hash output
|
||||
if [ "$RELEASE_GPG_KEY" = "" ] && ! gpg2 --card-status >/dev/null 2>&1; then
|
||||
echo OpenPGP card not found!
|
||||
echo Please insert your PGP card and run this script again.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ensure we have the latest built version of leauto
|
||||
letsencrypt-auto-source/build.py
|
||||
|
||||
# and that it's signed correctly
|
||||
while ! openssl dgst -sha256 -verify $RELEASE_OPENSSL_PUBKEY -signature \
|
||||
letsencrypt-auto-source/letsencrypt-auto.sig \
|
||||
letsencrypt-auto-source/letsencrypt-auto ; do
|
||||
read -p "Please correctly sign letsencrypt-auto with offline-signrequest.sh"
|
||||
done
|
||||
|
||||
# This signature is not quite as strong, but easier for people to verify out of band
|
||||
gpg2 -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign --digest-algo sha256 letsencrypt-auto-source/letsencrypt-auto
|
||||
# We can't rename the openssl letsencrypt-auto.sig for compatibility reasons,
|
||||
# but we can use the right name for certbot-auto.asc from day one
|
||||
mv letsencrypt-auto-source/letsencrypt-auto.asc letsencrypt-auto-source/certbot-auto.asc
|
||||
|
||||
# copy leauto to the root, overwriting the previous release version
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto
|
||||
cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto
|
||||
|
||||
git add certbot-auto letsencrypt-auto letsencrypt-auto-source docs/cli-help.txt
|
||||
git diff --cached
|
||||
git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version"
|
||||
git tag --local-user "$RELEASE_GPG_KEY" --sign --message "Release $version" "$tag"
|
||||
|
||||
cd ..
|
||||
echo Now in $PWD
|
||||
name=${root_without_le%.*}
|
||||
ext="${root_without_le##*.}"
|
||||
rev="$(git rev-parse --short HEAD)"
|
||||
echo tar cJvf $name.$rev.tar.xz $name.$rev
|
||||
echo gpg2 -U $RELEASE_GPG_KEY --detach-sign --armor $name.$rev.tar.xz
|
||||
cd ~-
|
||||
|
||||
echo "New root: $root"
|
||||
echo "Test commands (in the letstest repo):"
|
||||
echo 'python multitester.py targets.yaml $AWS_KEY $USERNAME scripts/test_leauto_upgrades.sh --alt_pip $YOUR_PIP_REPO --branch public-beta'
|
||||
echo 'python multitester.py targets.yaml $AWK_KEY $USERNAME scripts/test_letsencrypt_auto_certonly_standalone.sh --branch candidate-0.1.1'
|
||||
echo 'python multitester.py --saveinstances targets.yaml $AWS_KEY $USERNAME scripts/test_apache2.sh'
|
||||
echo "In order to upload packages run the following command:"
|
||||
echo twine upload "$root/dist.$version/*/*"
|
||||
|
||||
if [ "$RELEASE_BRANCH" = candidate-"$version" ] ; then
|
||||
SetVersion "$nextversion".dev0
|
||||
letsencrypt-auto-source/build.py
|
||||
git add letsencrypt-auto-source/letsencrypt-auto
|
||||
git diff
|
||||
git commit -m "Bump version to $nextversion"
|
||||
if ! command -v script >/dev/null 2>&1; then
|
||||
echo The command script was not found.
|
||||
echo Please install it.
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export RELEASE_DIR="./releases"
|
||||
mv "$RELEASE_DIR" "$RELEASE_DIR.$(date +%s).bak" || true
|
||||
LOG_PATH="log"
|
||||
mv "$LOG_PATH" "$LOG_PATH.$(date +%s).bak" || true
|
||||
|
||||
# Work with both Linux and macOS versions of script
|
||||
if script --help | grep -q -- '--command'; then
|
||||
script --command "tools/_release.sh $1 $2" "$LOG_PATH"
|
||||
else
|
||||
script "$LOG_PATH" tools/_release.sh "$1" "$2"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in a new issue