Merge remote-tracking branch 'origin/master' into explain_no_ip_certs

This commit is contained in:
Seth Schoen
2016-02-16 12:17:43 -08:00
27 changed files with 427 additions and 257 deletions
+6 -6
View File
@@ -38,20 +38,20 @@ matrix:
env: TOXENV=py27 BOULDER_INTEGRATION=1 env: TOXENV=py27 BOULDER_INTEGRATION=1
- python: "2.7" - python: "2.7"
env: TOXENV=py27-oldest BOULDER_INTEGRATION=1 env: TOXENV=py27-oldest BOULDER_INTEGRATION=1
- python: "2.7"
env: TOXENV=cover
- python: "2.7" - python: "2.7"
env: TOXENV=lint env: TOXENV=lint
- sudo: required
env: TOXENV=le_auto
services: docker
before_install:
- python: "2.7"
env: TOXENV=cover
- python: "3.3" - python: "3.3"
env: TOXENV=py33 env: TOXENV=py33
- python: "3.4" - python: "3.4"
env: TOXENV=py34 env: TOXENV=py34
- python: "3.5" - python: "3.5"
env: TOXENV=py35 env: TOXENV=py35
- sudo: required
env: TOXENV=le_auto
services: docker
before_install:
# Only build pushes to the master branch, PRs, and branches beginning with # Only build pushes to the master branch, PRs, and branches beginning with
# `test-`. This reduces the number of simultaneous Travis runs, which speeds # `test-`. This reduces the number of simultaneous Travis runs, which speeds
+1 -1
View File
@@ -58,7 +58,7 @@ RUN virtualenv --no-site-packages -p python2 /opt/letsencrypt/venv && \
-e /opt/letsencrypt/src/letsencrypt-nginx \ -e /opt/letsencrypt/src/letsencrypt-nginx \
-e /opt/letsencrypt/src/letshelp-letsencrypt \ -e /opt/letsencrypt/src/letshelp-letsencrypt \
-e /opt/letsencrypt/src/letsencrypt-compatibility-test \ -e /opt/letsencrypt/src/letsencrypt-compatibility-test \
-e /opt/letsencrypt/src[dev,docs,testing] -e /opt/letsencrypt/src[dev,docs]
# install in editable mode (-e) to save space: it's not possible to # install in editable mode (-e) to save space: it's not possible to
# "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image); # "rm -rf /opt/letsencrypt/src" (it's stays in the underlaying image);
+4 -4
View File
@@ -51,11 +51,11 @@ client will guide you through the process of obtaining and installing certs
interactively. interactively.
You can also tell it exactly what you want it to do from the command line. You can also tell it exactly what you want it to do from the command line.
For instance, if you want to obtain a cert for ``thing.com``, For instance, if you want to obtain a cert for ``example.com``,
``www.thing.com``, and ``otherthing.net``, using the Apache plugin to both ``www.example.com``, and ``other.example.net``, using the Apache plugin to both
obtain and install the certs, you could do this:: obtain and install the certs, you could do this::
./letsencrypt-auto --apache -d thing.com -d www.thing.com -d otherthing.net ./letsencrypt-auto --apache -d example.com -d www.example.com -d other.example.net
(The first time you run the command, it will make an account, and ask for an (The first time you run the command, it will make an account, and ask for an
email and agreement to the Let's Encrypt Subscriber Agreement; you can email and agreement to the Let's Encrypt Subscriber Agreement; you can
@@ -64,7 +64,7 @@ automate those with ``--email`` and ``--agree-tos``)
If you want to use a webserver that doesn't have full plugin support yet, you If you want to use a webserver that doesn't have full plugin support yet, you
can still use "standalone" or "webroot" plugins to obtain a certificate:: can still use "standalone" or "webroot" plugins to obtain a certificate::
./letsencrypt-auto certonly --standalone --email admin@thing.com -d thing.com -d www.thing.com -d otherthing.net ./letsencrypt-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net
Understanding the client in more depth Understanding the client in more depth
Vendored
+9
View File
@@ -5,10 +5,19 @@
VAGRANTFILE_API_VERSION = "2" VAGRANTFILE_API_VERSION = "2"
# Setup instructions from docs/contributing.rst # Setup instructions from docs/contributing.rst
# Script installs dependencies for tox and boulder integration
$ubuntu_setup_script = <<SETUP_SCRIPT $ubuntu_setup_script = <<SETUP_SCRIPT
cd /vagrant cd /vagrant
./letsencrypt-auto-source/letsencrypt-auto --os-packages-only ./letsencrypt-auto-source/letsencrypt-auto --os-packages-only
./tools/venv.sh ./tools/venv.sh
wget https://storage.googleapis.com/golang/go1.5.3.linux-amd64.tar.gz -P /tmp/
sudo tar -C /usr/local -xzf /tmp/go1.5.3.linux-amd64.tar.gz
if ! grep -Fxq "export GOROOT=/usr/local/go" /home/vagrant/.profile ; then echo "export GOROOT=/usr/local/go" >> /home/vagrant/.profile; fi
if ! grep -Fxq "export PATH=\\$GOROOT/bin:\\$PATH" /home/vagrant/.profile ; then echo "export PATH=\\$GOROOT/bin:\\$PATH" >> /home/vagrant/.profile; fi
if ! grep -Fxq "export GOPATH=\\$HOME/go" /home/vagrant/.profile ; then echo "export GOPATH=\\$HOME/go" >> /home/vagrant/.profile; fi
if ! grep -Fxq "cd /vagrant/; ./tests/boulder-start.sh &" /etc/rc.local ; then sed -i -e '$i \cd /vagrant/; ./tests/boulder-start.sh &\n' /etc/rc.local; fi
export DEBIAN_FRONTEND=noninteractive
sudo -E apt-get -q -y install git make libltdl-dev mariadb-server rabbitmq-server nginx-light
SETUP_SCRIPT SETUP_SCRIPT
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+11 -10
View File
@@ -180,40 +180,41 @@ class Client(object): # pylint: disable=too-many-instance-attributes
raise errors.UnexpectedUpdate(authzr) raise errors.UnexpectedUpdate(authzr)
return authzr return authzr
def request_challenges(self, identifier, new_authzr_uri): def request_challenges(self, identifier, new_authzr_uri=None):
"""Request challenges. """Request challenges.
:param identifier: Identifier to be challenged. :param .messages.Identifier identifier: Identifier to be challenged.
:type identifier: `.messages.Identifier` :param str new_authzr_uri: ``new-authorization`` URI. If omitted,
will default to value found in ``directory``.
:param str new_authzr_uri: new-authorization URI
:returns: Authorization Resource. :returns: Authorization Resource.
:rtype: `.AuthorizationResource` :rtype: `.AuthorizationResource`
""" """
new_authz = messages.NewAuthorization(identifier=identifier) new_authz = messages.NewAuthorization(identifier=identifier)
response = self.net.post(new_authzr_uri, new_authz) response = self.net.post(self.directory.new_authz
if new_authzr_uri is None else new_authzr_uri,
new_authz)
# TODO: handle errors # TODO: handle errors
assert response.status_code == http_client.CREATED assert response.status_code == http_client.CREATED
return self._authzr_from_response(response, identifier) return self._authzr_from_response(response, identifier)
def request_domain_challenges(self, domain, new_authz_uri): def request_domain_challenges(self, domain, new_authzr_uri=None):
"""Request challenges for domain names. """Request challenges for domain names.
This is simply a convenience function that wraps around This is simply a convenience function that wraps around
`request_challenges`, but works with domain names instead of `request_challenges`, but works with domain names instead of
generic identifiers. generic identifiers. See ``request_challenges`` for more
documentation.
:param str domain: Domain name to be challenged. :param str domain: Domain name to be challenged.
:param str new_authzr_uri: new-authorization URI
:returns: Authorization Resource. :returns: Authorization Resource.
:rtype: `.AuthorizationResource` :rtype: `.AuthorizationResource`
""" """
return self.request_challenges(messages.Identifier( return self.request_challenges(messages.Identifier(
typ=messages.IDENTIFIER_FQDN, value=domain), new_authz_uri) typ=messages.IDENTIFIER_FQDN, value=domain), new_authzr_uri)
def answer_challenge(self, challb, response): def answer_challenge(self, challb, response):
"""Answer challenge. """Answer challenge.
+25 -8
View File
@@ -38,6 +38,8 @@ class ClientTest(unittest.TestCase):
'https://www.letsencrypt-demo.org/acme/new-reg', 'https://www.letsencrypt-demo.org/acme/new-reg',
messages.Revocation: messages.Revocation:
'https://www.letsencrypt-demo.org/acme/revoke-cert', 'https://www.letsencrypt-demo.org/acme/revoke-cert',
messages.NewAuthorization:
'https://www.letsencrypt-demo.org/acme/new-authz',
}) })
from acme.client import Client from acme.client import Client
@@ -142,7 +144,7 @@ class ClientTest(unittest.TestCase):
regr = self.client.update_registration.call_args[0][0] regr = self.client.update_registration.call_args[0][0]
self.assertEqual(self.regr.terms_of_service, regr.body.agreement) self.assertEqual(self.regr.terms_of_service, regr.body.agreement)
def test_request_challenges(self): def _prepare_response_for_request_challenges(self):
self.response.status_code = http_client.CREATED self.response.status_code = http_client.CREATED
self.response.headers['Location'] = self.authzr.uri self.response.headers['Location'] = self.authzr.uri
self.response.json.return_value = self.authz.to_json() self.response.json.return_value = self.authz.to_json()
@@ -150,10 +152,20 @@ class ClientTest(unittest.TestCase):
'next': {'url': self.authzr.new_cert_uri}, 'next': {'url': self.authzr.new_cert_uri},
} }
self.client.request_challenges(self.identifier, self.authzr.uri) def test_request_challenges(self):
# TODO: test POST call arguments self._prepare_response_for_request_challenges()
self.client.request_challenges(self.identifier)
self.net.post.assert_called_once_with(
self.directory.new_authz,
messages.NewAuthorization(identifier=self.identifier))
# TODO: split here and separate test def test_requets_challenges_custom_uri(self):
self._prepare_response_for_request_challenges()
self.client.request_challenges(self.identifier, 'URI')
self.net.post.assert_called_once_with('URI', mock.ANY)
def test_request_challenges_unexpected_update(self):
self._prepare_response_for_request_challenges()
self.response.json.return_value = self.authz.update( self.response.json.return_value = self.authz.update(
identifier=self.identifier.update(value='foo')).to_json() identifier=self.identifier.update(value='foo')).to_json()
self.assertRaises( self.assertRaises(
@@ -162,15 +174,20 @@ class ClientTest(unittest.TestCase):
def test_request_challenges_missing_next(self): def test_request_challenges_missing_next(self):
self.response.status_code = http_client.CREATED self.response.status_code = http_client.CREATED
self.assertRaises( self.assertRaises(errors.ClientError, self.client.request_challenges,
errors.ClientError, self.client.request_challenges, self.identifier)
self.identifier, self.regr)
def test_request_domain_challenges(self): def test_request_domain_challenges(self):
self.client.request_challenges = mock.MagicMock() self.client.request_challenges = mock.MagicMock()
self.assertEqual( self.assertEqual(
self.client.request_challenges(self.identifier), self.client.request_challenges(self.identifier),
self.client.request_domain_challenges('example.com', self.regr)) self.client.request_domain_challenges('example.com'))
def test_request_domain_challenges_custom_uri(self):
self.client.request_challenges = mock.MagicMock()
self.assertEqual(
self.client.request_challenges(self.identifier, 'URI'),
self.client.request_domain_challenges('example.com', 'URI'))
def test_answer_challenge(self): def test_answer_challenge(self):
self.response.links['up'] = {'url': self.challr.authzr_uri} self.response.links['up'] = {'url': self.challr.authzr_uri}
+7 -6
View File
@@ -34,17 +34,18 @@ if sys.version_info < (2, 7):
else: else:
install_requires.append('mock') install_requires.append('mock')
dev_extras = [
'nose',
'pep8',
'tox',
]
docs_extras = [ docs_extras = [
'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags 'Sphinx>=1.0', # autodoc_member_order = 'bysource', autodoc_default_flags
'sphinx_rtd_theme', 'sphinx_rtd_theme',
'sphinxcontrib-programoutput', 'sphinxcontrib-programoutput',
] ]
testing_extras = [
'nose',
'tox',
]
setup( setup(
name='acme', name='acme',
@@ -74,8 +75,8 @@ setup(
include_package_data=True, include_package_data=True,
install_requires=install_requires, install_requires=install_requires,
extras_require={ extras_require={
'dev': dev_extras,
'docs': docs_extras, 'docs': docs_extras,
'testing': testing_extras,
}, },
entry_points={ entry_points={
'console_scripts': [ 'console_scripts': [
+1 -1
View File
@@ -139,7 +139,7 @@ client's priorities. The Mozilla security team is likely to have more
resources and expertise to bring to bear on evaluating reasons why its resources and expertise to bring to bear on evaluating reasons why its
recommendations should be updated. recommendations should be updated.
The Let's Encrpyt project will entertain proposals to create a *very* The Let's Encrypt project will entertain proposals to create a *very*
small number of alternative configurations (apart from Modern, small number of alternative configurations (apart from Modern,
Intermediate, and Old) that there's reason to believe would be widely Intermediate, and Old) that there's reason to believe would be widely
used by sysadmins; this would usually be a preferable course to modifying used by sysadmins; this would usually be a preferable course to modifying
+8
View File
@@ -96,6 +96,14 @@ Integration testing with the boulder CA
Generally it is sufficient to open a pull request and let Github and Travis run Generally it is sufficient to open a pull request and let Github and Travis run
integration tests for you. integration tests for you.
However, if you prefer to run tests, you can use Vagrant, using the Vagrantfile
in Let's Encrypt's repository. To execute the tests on a Vagrant box, the only
command you are required to run is::
./tests/boulder-integration.sh
Otherwise, please follow the following instructions.
Mac OS X users: Run ``./tests/mac-bootstrap.sh`` instead of Mac OS X users: Run ``./tests/mac-bootstrap.sh`` instead of
``boulder-start.sh`` to install dependencies, configure the ``boulder-start.sh`` to install dependencies, configure the
environment, and start boulder. environment, and start boulder.
+148 -48
View File
@@ -49,7 +49,7 @@ or for full help, type:
``letsencrypt-auto`` is the recommended method of running the Let's Encrypt ``letsencrypt-auto`` is the recommended method of running the Let's Encrypt
client beta releases on systems that don't have a packaged version. Debian, client beta releases on systems that don't have a packaged version. Debian,
Arch linux, FreeBSD, and OpenBSD now have native packages, so on those Arch Linux, Gentoo, FreeBSD, and OpenBSD now have native packages, so on those
systems you can just install ``letsencrypt`` (and perhaps systems you can just install ``letsencrypt`` (and perhaps
``letsencrypt-apache``). If you'd like to run the latest copy from Git, or ``letsencrypt-apache``). If you'd like to run the latest copy from Git, or
run your own locally modified copy of the client, follow the instructions in run your own locally modified copy of the client, follow the instructions in
@@ -71,7 +71,9 @@ Plugin Auth Inst Notes
=========== ==== ==== =============================================================== =========== ==== ==== ===============================================================
apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on
Debian-based distributions with ``libaugeas0`` 1.0+. Debian-based distributions with ``libaugeas0`` 1.0+.
standalone_ Y N Uses a "standalone" webserver to obtain a cert. standalone_ Y N Uses a "standalone" webserver to obtain a cert. This is useful
on systems with no webserver, or when direct integration with
the local webserver is not supported or not desired.
webroot_ Y N Obtains a cert by writing to the webroot directory of an webroot_ Y N Obtains a cert by writing to the webroot directory of an
already running webserver. already running webserver.
manual_ Y N Helps you obtain a cert by giving you instructions to perform manual_ Y N Helps you obtain a cert by giving you instructions to perform
@@ -91,6 +93,46 @@ This automates both obtaining *and* installing certs on an Apache
webserver. To specify this plugin on the command line, simply include webserver. To specify this plugin on the command line, simply include
``--apache``. ``--apache``.
Webroot
-------
If you're running a local webserver for which you have the ability
to modify the content being served, and you'd prefer not to stop the
webserver during the certificate issuance process, you can use the webroot
plugin to obtain a cert by including ``certonly`` and ``--webroot`` on
the command line. In addition, you'll need to specify ``--webroot-path``
or ``-w`` with the top-level directory ("web root") containing the files
served by your webserver. For example, ``--webroot-path /var/www/html``
or ``--webroot-path /usr/share/nginx/html`` are two common webroot paths.
If you're getting a certificate for many domains at once, the plugin
needs to know where each domain's files are served from, which could
potentially be a separate directory for each domain. When requested a
certificate for multiple domains, each domain will use the most recently
specified ``--webroot-path``. So, for instance,
``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net``
would obtain a single certificate for all of those names, using the
``/var/www/example`` webroot directory for the first two, and
``/var/www/other`` for the second two.
The webroot plugin works by creating a temporary file for each of your requested
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's
Encrypt validation server makes HTTP requests to validate that the DNS for each
requested domain resolves to the server running letsencrypt. An example request
made to your web server would look like:
::
66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
Note that to use the webroot plugin, your server must be configured to serve
files from hidden directories. If ``/.well-known`` is treated specially by
your webserver configuration, you might need to modify the configuration
to ensure that files inside ``/.well-known/acme-challenge`` are served by
the webserver.
Standalone Standalone
---------- ----------
@@ -104,39 +146,10 @@ one of the options shown below on the command line.
* ``--standalone-supported-challenges http-01`` to use port 80 * ``--standalone-supported-challenges http-01`` to use port 80
* ``--standalone-supported-challenges tls-sni-01`` to use port 443 * ``--standalone-supported-challenges tls-sni-01`` to use port 443
Webroot The standalone plugin does not rely on any other server software running
------- on the machine where you obtain the certificate. It must still be possible
for that machine to accept inbound connections from the Internet on the
If you're running a webserver that you don't want to stop to use specified port using each requested domain name.
standalone, you can use the webroot plugin to obtain a cert by
including ``certonly`` and ``--webroot`` on the command line. In
addition, you'll need to specify ``--webroot-path`` or ``-w`` with the root
directory of the files served by your webserver. For example,
``--webroot-path /var/www/html`` or
``--webroot-path /usr/share/nginx/html`` are two common webroot paths.
If you're getting a certificate for many domains at once, each domain will use
the most recent ``--webroot-path``. So for instance:
``letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/eg -d eg.is -d www.eg.is``
Would obtain a single certificate for all of those names, using the
``/var/www/example`` webroot directory for the first two, and
``/var/www/eg`` for the second two.
The webroot plugin works by creating a temporary file for each of your requested
domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's
Encrypt validation server makes HTTP requests to validate that the DNS for each
requested domain resolves to the server running letsencrypt. An example request
made to your web server would look like:
::
66.133.109.36 - - [05/Jan/2016:20:11:24 -0500] "GET /.well-known/acme-challenge/HGr8U1IeTW4kY_Z6UIyaakzOkyQgPr_7ArlLgtZE8SX HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
Note that to use the webroot plugin, your server must be configured to serve
files from hidden directories.
Manual Manual
------ ------
@@ -146,7 +159,8 @@ other than your target webserver or perform the steps for domain
validation yourself, you can use the manual plugin. While hidden from validation yourself, you can use the manual plugin. While hidden from
the UI, you can use the plugin to obtain a cert by specifying the UI, you can use the plugin to obtain a cert by specifying
``certonly`` and ``--manual`` on the command line. This requires you ``certonly`` and ``--manual`` on the command line. This requires you
to copy and paste commands into another terminal session. to copy and paste commands into another terminal session, which may
be on a different computer.
Nginx Nginx
----- -----
@@ -157,7 +171,7 @@ is still experimental, however, and is not installed with
letsencrypt-auto_. If installed, you can select this plugin on the letsencrypt-auto_. If installed, you can select this plugin on the
command line by including ``--nginx``. command line by including ``--nginx``.
Third party plugins Third-party plugins
------------------- -------------------
These plugins are listed at These plugins are listed at
@@ -171,21 +185,61 @@ Renewal
days). Make sure you renew the certificates at least once in 3 days). Make sure you renew the certificates at least once in 3
months. months.
In order to renew certificates simply call the ``letsencrypt`` (or The ``letsencrypt`` client now supports a ``renew`` action to check
letsencrypt-auto_) again, and use the same values when prompted. You can all installed certificates for impending expiry and attempt to renew
automate it slightly by passing necessary flags on the CLI (see `--help them. The simplest form is simply
all`), or even further using the :ref:`config-file`. The ``--force-renew``
flag may be helpful for automating renewal; it causes the expiration time ``letsencrypt renew``
of the certificate(s) to be ignored when considering renewal. If you're
sure that UI doesn't prompt for any details you can add the command to This will attempt to renew any previously-obtained certificates that
``crontab`` (make it less than every 90 days to avoid problems, say expire in less than 30 days. The same plugin and options that were used
every month). at the time the certificate was originally issued will be used for the
renewal attempt, unless you specify other plugins or options.
If you're sure that this command executes successfully without human
intervention, you can add the command to ``crontab`` (since certificates
are only renewed when they're determined to be near expiry, the command
can run on a regular basis, like every week or every day); note that
the current version provides detailed output describing either renewal
success or failure.
The ``--force-renew`` flag may be helpful for automating renewal;
it causes the expiration time of the certificate(s) to be ignored when
considering renewal, and attempts to renew each and every installed
certificate regardless of its age. (This form is not appropriate to run
daily because each certificate will be renewed every day, which will
quickly run into the certificate authority rate limit.)
Note that options provided to ``letsencrypt renew`` will apply to
*every* certificate for which renewal is attempted; for example,
``letsencrypt renew --rsa-key-size 4096`` would try to replace every
near-expiry certificate with an equivalent certificate using a 4096-bit
RSA public key. If a certificate is successfully renewed using
specified options, those options will be saved and used for future
renewals of that certificate.
An alternative form that provides for more fine-grained control over the
renewal process (while renewing specified certificates one at a time),
is ``letsencrypt certonly`` with the complete set of subject domains of
a specific certificate specified via `-d` flags, like
``letsencrypt certonly -d example.com -d www.example.com``
(All of the domains covered by the certificate must be specified in
this case in order to renew and replace the old certificate rather
than obtaining a new one; don't forget any `www.` domains! Specifying
a subset of the domains creates a new, separate certificate containing
only those domains, rather than replacing the original certificate.)
The ``certonly`` form attempts to renew one individual certificate.
Please note that the CA will send notification emails to the address Please note that the CA will send notification emails to the address
you provide if you do not renew certificates that are about to expire. you provide if you do not renew certificates that are about to expire.
Let's Encrypt is working hard on automating the renewal process. Until Let's Encrypt is working hard on improving the renewal process, and we
the tool is ready, we are sorry for the inconvenience! apologize for any inconveniences you encounter in integrating these
commands into your individual environment.
.. _where-certs: .. _where-certs:
@@ -375,6 +429,52 @@ If you don't want to use the Apache plugin, you can omit the
Packages for Debian Jessie are coming in the next few weeks. Packages for Debian Jessie are coming in the next few weeks.
**Gentoo**
The official Let's Encrypt client is available in Gentoo Portage. If you
want to use the Apache plugin, it has to be installed separately:
.. code-block:: shell
emerge -av app-crypt/letsencrypt
emerge -av app-crypt/letsencrypt-apache
Currently, only the Apache plugin is included in Portage. However, if you
want the nginx plugin, you can use Layman to add the mrueg overlay which
does include the nginx plugin package:
.. code-block:: shell
emerge -av app-portage/layman
layman -S
layman -a mrueg
emerge -av app-crypt/letsencrypt-nginx
When using the Apache plugin, you will run into a "cannot find a cert or key
directive" error if you're sporting the default Gentoo ``httpd.conf``.
You can fix this by commenting out two lines in ``/etc/apache2/httpd.conf``
as follows:
Change
.. code-block:: shell
<IfDefine SSL>
LoadModule ssl_module modules/mod_ssl.so
</IfDefine>
to
.. code-block:: shell
#<IfDefine SSL>
LoadModule ssl_module modules/mod_ssl.so
#</IfDefine>
For the time being, this is the only way for the Apache plugin to recognise
the appropriate directives when installing the certificate.
Note: this change is not required for the other plugins.
**Other Operating Systems** **Other Operating Systems**
OS packaging is an ongoing effort. If you'd like to package OS packaging is an ongoing effort. If you'd like to package
+1 -1
View File
@@ -17,7 +17,7 @@ RUN apt-get update && \
apt-get clean apt-get clean
RUN pip install nose RUN pip install nose
RUN mkdir -p /home/lea/letsencrypt/letsencrypt RUN mkdir -p /home/lea/letsencrypt
# Install fake testing CA: # Install fake testing CA:
COPY ./tests/certs/ca/my-root-ca.crt.pem /usr/local/share/ca-certificates/ COPY ./tests/certs/ca/my-root-ca.crt.pem /usr/local/share/ca-certificates/
+74 -58
View File
@@ -18,25 +18,31 @@ set -e # Work even if somebody does "sh thisscript.sh".
XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
VENV_NAME="letsencrypt" VENV_NAME="letsencrypt"
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
VENV_BIN=${VENV_PATH}/bin VENV_BIN="$VENV_PATH/bin"
LE_AUTO_VERSION="0.4.0" LE_AUTO_VERSION="0.5.0.dev0"
# This script takes the same arguments as the main letsencrypt program, but it # This script takes the same arguments as the main letsencrypt program, but it
# additionally responds to --verbose (more output) and --debug (allow support # additionally responds to --verbose (more output) and --debug (allow support
# for experimental platforms) # for experimental platforms)
for arg in "$@" ; do for arg in "$@" ; do
# This first clause is redundant with the third, but hedging on portability case "$arg" in
if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then --debug)
VERBOSE=1 DEBUG=1;;
elif [ "$arg" = "--no-self-upgrade" ] ; then --os-packages-only)
# Do not upgrade this script (also prevents client upgrades, because each OS_PACKAGES_ONLY=1;;
# copy of the script pins a hash of the python client) --no-self-upgrade)
NO_SELF_UPGRADE=1 # Do not upgrade this script (also prevents client upgrades, because each
elif [ "$arg" = "--os-packages-only" ] ; then # copy of the script pins a hash of the python client)
OS_PACKAGES_ONLY=1 NO_SELF_UPGRADE=1;;
elif [ "$arg" = "--debug" ]; then --verbose)
DEBUG=1 VERBOSE=1;;
fi [!-]*|-*[!v]*|-)
# Anything that isn't -v, -vv, etc.: that is, anything that does not
# start with a -, contains anything that's not a v, or is just "-"
;;
*) # -v+ remains.
VERBOSE=1;;
esac
done done
# letsencrypt-auto needs root access to bootstrap OS dependencies, and # letsencrypt-auto needs root access to bootstrap OS dependencies, and
@@ -91,21 +97,18 @@ ExperimentalBootstrap() {
} }
DeterminePythonVersion() { DeterminePythonVersion() {
if command -v python2.7 > /dev/null ; then for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do
export LE_PYTHON=${LE_PYTHON:-python2.7} # Break (while keeping the LE_PYTHON value) if found.
elif command -v python27 > /dev/null ; then command -v "$LE_PYTHON" > /dev/null && break
export LE_PYTHON=${LE_PYTHON:-python27} done
elif command -v python2 > /dev/null ; then if [ "$?" != "0" ]; then
export LE_PYTHON=${LE_PYTHON:-python2} echo "Cannot find any Pythons; please install one!"
elif command -v python > /dev/null ; then
export LE_PYTHON=${LE_PYTHON:-python}
else
echo "Cannot find any Pythons... please install one!"
exit 1 exit 1
fi fi
export LE_PYTHON
PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
if [ $PYVER -lt 26 ]; then if [ "$PYVER" -lt 26 ]; then
echo "You have an ancient version of Python entombed in your operating system..." echo "You have an ancient version of Python entombed in your operating system..."
echo "This isn't going to work; you'll need at least version 2.6." echo "This isn't going to work; you'll need at least version 2.6."
exit 1 exit 1
@@ -324,13 +327,13 @@ BootstrapGentooCommon() {
case "$PACKAGE_MANAGER" in case "$PACKAGE_MANAGER" in
(paludis) (paludis)
"$SUDO" cave resolve --keep-targets if-possible $PACKAGES -x "$SUDO" cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x
;; ;;
(pkgcore) (pkgcore)
"$SUDO" pmerge --noreplace $PACKAGES "$SUDO" pmerge --noreplace --oneshot $PACKAGES
;; ;;
(portage|*) (portage|*)
"$SUDO" emerge --noreplace $PACKAGES "$SUDO" emerge --noreplace --oneshot $PACKAGES
;; ;;
esac esac
} }
@@ -345,20 +348,27 @@ BootstrapFreeBsd() {
BootstrapMac() { BootstrapMac() {
if ! hash brew 2>/dev/null; then if ! hash brew 2>/dev/null; then
echo "Homebrew Not Installed\nDownloading..." echo "Homebrew not installed.\nDownloading..."
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
fi fi
brew install augeas if [ -z "$(brew list --versions augeas)" ]; then
brew install dialog echo "augeas not installed.\nInstalling augeas from Homebrew..."
brew install augeas
fi
if [ -z "$(brew list --versions dialog)" ]; then
echo "dialog not installed.\nInstalling dialog from Homebrew..."
brew install dialog
fi
if ! hash pip 2>/dev/null; then if ! hash pip 2>/dev/null; then
echo "pip Not Installed\nInstalling python from Homebrew..." echo "pip not installed.\nInstalling python from Homebrew..."
brew install python brew install python
fi fi
if ! hash virtualenv 2>/dev/null; then if ! hash virtualenv 2>/dev/null; then
echo "virtualenv Not Installed\nInstalling with pip" echo "virtualenv not installed.\nInstalling with pip..."
pip install virtualenv pip install virtualenv
fi fi
} }
@@ -412,9 +422,10 @@ TempDir() {
if [ "$NO_SELF_UPGRADE" = 1 ]; then if [ "$1" = "--le-auto-phase2" ]; then
# Phase 2: Create venv, install LE, and run. # Phase 2: Create venv, install LE, and run.
shift 1 # the --le-auto-phase2 arg
if [ -f "$VENV_BIN/letsencrypt" ]; then if [ -f "$VENV_BIN/letsencrypt" ]; then
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2)
else else
@@ -1630,8 +1641,10 @@ UNLIKELY_EOF
# Report error. (Otherwise, be quiet.) # Report error. (Otherwise, be quiet.)
echo "Had a problem while downloading and verifying Python packages:" echo "Had a problem while downloading and verifying Python packages:"
echo "$PEEP_OUT" echo "$PEEP_OUT"
rm -rf "$VENV_PATH"
exit 1 exit 1
fi fi
echo "Installation succeeded."
fi fi
echo "Requesting root privileges to run letsencrypt..." echo "Requesting root privileges to run letsencrypt..."
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
@@ -1653,10 +1666,11 @@ else
exit 0 exit 0
fi fi
echo "Checking for new version..." if [ "$NO_SELF_UPGRADE" != 1 ]; then
TEMP_DIR=$(TempDir) echo "Checking for new version..."
# --------------------------------------------------------------------------- TEMP_DIR=$(TempDir)
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" # ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
"""Do downloading and JSON parsing without additional dependencies. :: """Do downloading and JSON parsing without additional dependencies. ::
# Print latest released version of LE to stdout: # Print latest released version of LE to stdout:
@@ -1785,25 +1799,27 @@ if __name__ == '__main__':
exit(main()) exit(main())
UNLIKELY_EOF UNLIKELY_EOF
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
DeterminePythonVersion DeterminePythonVersion
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
# Now we drop into Python so we don't have to install even more # Now we drop into Python so we don't have to install even more
# dependencies (curl, etc.), for better flow control, and for the option of # dependencies (curl, etc.), for better flow control, and for the option of
# future Windows compatibility. # future Windows compatibility.
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
# Install new copy of letsencrypt-auto. This preserves permissions and # Install new copy of letsencrypt-auto. This preserves permissions and
# ownership from the old copy. # ownership from the old copy.
# TODO: Deal with quotes in pathnames. # TODO: Deal with quotes in pathnames.
echo "Replacing letsencrypt-auto..." echo "Replacing letsencrypt-auto..."
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
# TODO: Clean up temp dir safely, even if it has quotes in its path. # TODO: Clean up temp dir safely, even if it has quotes in its path.
rm -rf "$TEMP_DIR" rm -rf "$TEMP_DIR"
fi # should upgrade fi # A newer version is available.
"$0" --no-self-upgrade "$@" fi # Self-upgrading is allowed.
"$0" --le-auto-phase2 "$@"
fi fi
@@ -18,25 +18,31 @@ set -e # Work even if somebody does "sh thisscript.sh".
XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
VENV_NAME="letsencrypt" VENV_NAME="letsencrypt"
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
VENV_BIN=${VENV_PATH}/bin VENV_BIN="$VENV_PATH/bin"
LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}" LE_AUTO_VERSION="{{ LE_AUTO_VERSION }}"
# This script takes the same arguments as the main letsencrypt program, but it # This script takes the same arguments as the main letsencrypt program, but it
# additionally responds to --verbose (more output) and --debug (allow support # additionally responds to --verbose (more output) and --debug (allow support
# for experimental platforms) # for experimental platforms)
for arg in "$@" ; do for arg in "$@" ; do
# This first clause is redundant with the third, but hedging on portability case "$arg" in
if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then --debug)
VERBOSE=1 DEBUG=1;;
elif [ "$arg" = "--no-self-upgrade" ] ; then --os-packages-only)
# Do not upgrade this script (also prevents client upgrades, because each OS_PACKAGES_ONLY=1;;
# copy of the script pins a hash of the python client) --no-self-upgrade)
NO_SELF_UPGRADE=1 # Do not upgrade this script (also prevents client upgrades, because each
elif [ "$arg" = "--os-packages-only" ] ; then # copy of the script pins a hash of the python client)
OS_PACKAGES_ONLY=1 NO_SELF_UPGRADE=1;;
elif [ "$arg" = "--debug" ]; then --verbose)
DEBUG=1 VERBOSE=1;;
fi [!-]*|-*[!v]*|-)
# Anything that isn't -v, -vv, etc.: that is, anything that does not
# start with a -, contains anything that's not a v, or is just "-"
;;
*) # -v+ remains.
VERBOSE=1;;
esac
done done
# letsencrypt-auto needs root access to bootstrap OS dependencies, and # letsencrypt-auto needs root access to bootstrap OS dependencies, and
@@ -91,21 +97,18 @@ ExperimentalBootstrap() {
} }
DeterminePythonVersion() { DeterminePythonVersion() {
if command -v python2.7 > /dev/null ; then for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do
export LE_PYTHON=${LE_PYTHON:-python2.7} # Break (while keeping the LE_PYTHON value) if found.
elif command -v python27 > /dev/null ; then command -v "$LE_PYTHON" > /dev/null && break
export LE_PYTHON=${LE_PYTHON:-python27} done
elif command -v python2 > /dev/null ; then if [ "$?" != "0" ]; then
export LE_PYTHON=${LE_PYTHON:-python2} echo "Cannot find any Pythons; please install one!"
elif command -v python > /dev/null ; then
export LE_PYTHON=${LE_PYTHON:-python}
else
echo "Cannot find any Pythons... please install one!"
exit 1 exit 1
fi fi
export LE_PYTHON
PYVER=`"$LE_PYTHON" --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
if [ $PYVER -lt 26 ]; then if [ "$PYVER" -lt 26 ]; then
echo "You have an ancient version of Python entombed in your operating system..." echo "You have an ancient version of Python entombed in your operating system..."
echo "This isn't going to work; you'll need at least version 2.6." echo "This isn't going to work; you'll need at least version 2.6."
exit 1 exit 1
@@ -168,9 +171,10 @@ TempDir() {
if [ "$NO_SELF_UPGRADE" = 1 ]; then if [ "$1" = "--le-auto-phase2" ]; then
# Phase 2: Create venv, install LE, and run. # Phase 2: Create venv, install LE, and run.
shift 1 # the --le-auto-phase2 arg
if [ -f "$VENV_BIN/letsencrypt" ]; then if [ -f "$VENV_BIN/letsencrypt" ]; then
INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2) INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | cut -d " " -f 2)
else else
@@ -207,8 +211,10 @@ UNLIKELY_EOF
# Report error. (Otherwise, be quiet.) # Report error. (Otherwise, be quiet.)
echo "Had a problem while downloading and verifying Python packages:" echo "Had a problem while downloading and verifying Python packages:"
echo "$PEEP_OUT" echo "$PEEP_OUT"
rm -rf "$VENV_PATH"
exit 1 exit 1
fi fi
echo "Installation succeeded."
fi fi
echo "Requesting root privileges to run letsencrypt..." echo "Requesting root privileges to run letsencrypt..."
echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" echo " " $SUDO "$VENV_BIN/letsencrypt" "$@"
@@ -230,31 +236,34 @@ else
exit 0 exit 0
fi fi
echo "Checking for new version..." if [ "$NO_SELF_UPGRADE" != 1 ]; then
TEMP_DIR=$(TempDir) echo "Checking for new version..."
# --------------------------------------------------------------------------- TEMP_DIR=$(TempDir)
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" # ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py"
{{ fetch.py }} {{ fetch.py }}
UNLIKELY_EOF UNLIKELY_EOF
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
DeterminePythonVersion DeterminePythonVersion
REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version`
if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then
echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..."
# Now we drop into Python so we don't have to install even more # Now we drop into Python so we don't have to install even more
# dependencies (curl, etc.), for better flow control, and for the option of # dependencies (curl, etc.), for better flow control, and for the option of
# future Windows compatibility. # future Windows compatibility.
"$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION"
# Install new copy of letsencrypt-auto. This preserves permissions and # Install new copy of letsencrypt-auto. This preserves permissions and
# ownership from the old copy. # ownership from the old copy.
# TODO: Deal with quotes in pathnames. # TODO: Deal with quotes in pathnames.
echo "Replacing letsencrypt-auto..." echo "Replacing letsencrypt-auto..."
echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
$SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0" $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$0"
# TODO: Clean up temp dir safely, even if it has quotes in its path. # TODO: Clean up temp dir safely, even if it has quotes in its path.
rm -rf "$TEMP_DIR" rm -rf "$TEMP_DIR"
fi # should upgrade fi # A newer version is available.
"$0" --no-self-upgrade "$@" fi # Self-upgrading is allowed.
"$0" --le-auto-phase2 "$@"
fi fi
@@ -11,13 +11,13 @@ BootstrapGentooCommon() {
case "$PACKAGE_MANAGER" in case "$PACKAGE_MANAGER" in
(paludis) (paludis)
"$SUDO" cave resolve --keep-targets if-possible $PACKAGES -x "$SUDO" cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x
;; ;;
(pkgcore) (pkgcore)
"$SUDO" pmerge --noreplace $PACKAGES "$SUDO" pmerge --noreplace --oneshot $PACKAGES
;; ;;
(portage|*) (portage|*)
"$SUDO" emerge --noreplace $PACKAGES "$SUDO" emerge --noreplace --oneshot $PACKAGES
;; ;;
esac esac
} }
@@ -1,19 +1,26 @@
BootstrapMac() { BootstrapMac() {
if ! hash brew 2>/dev/null; then if ! hash brew 2>/dev/null; then
echo "Homebrew Not Installed\nDownloading..." echo "Homebrew not installed.\nDownloading..."
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
fi fi
brew install augeas if [ -z "$(brew list --versions augeas)" ]; then
brew install dialog echo "augeas not installed.\nInstalling augeas from Homebrew..."
brew install augeas
fi
if [ -z "$(brew list --versions dialog)" ]; then
echo "dialog not installed.\nInstalling dialog from Homebrew..."
brew install dialog
fi
if ! hash pip 2>/dev/null; then if ! hash pip 2>/dev/null; then
echo "pip Not Installed\nInstalling python from Homebrew..." echo "pip not installed.\nInstalling python from Homebrew..."
brew install python brew install python
fi fi
if ! hash virtualenv 2>/dev/null; then if ! hash virtualenv 2>/dev/null; then
echo "virtualenv Not Installed\nInstalling with pip" echo "virtualenv not installed.\nInstalling with pip..."
pip install virtualenv pip install virtualenv
fi fi
} }
+7 -1
View File
@@ -5,7 +5,7 @@ from contextlib import contextmanager
from functools import partial from functools import partial
from json import dumps from json import dumps
from os import chmod, environ from os import chmod, environ
from os.path import abspath, dirname, join from os.path import abspath, dirname, exists, join
import re import re
from shutil import copy, rmtree from shutil import copy, rmtree
import socket import socket
@@ -338,6 +338,12 @@ class AutoTests(TestCase):
self.assertIn("THE FOLLOWING PACKAGES DIDN'T MATCH THE " self.assertIn("THE FOLLOWING PACKAGES DIDN'T MATCH THE "
"HASHES SPECIFIED IN THE REQUIREMENTS", "HASHES SPECIFIED IN THE REQUIREMENTS",
exc.output) exc.output)
ok_(not exists(join(venv_dir, 'letsencrypt')),
msg="The virtualenv was left around, even though "
"installation didn't succeed. We shouldn't do "
"this, as it foils our detection of whether we "
"need to recreate the virtualenv, which hinges "
"on the presence of $VENV_BIN/letsencrypt.")
else: else:
self.fail("Peep didn't detect a bad hash and stop the " self.fail("Peep didn't detect a bad hash and stop the "
"installation.") "installation.")
+2 -14
View File
@@ -319,12 +319,11 @@ def _handle_identical_cert_request(config, cert):
elif config.verb == "certonly": elif config.verb == "certonly":
keep_opt = "Keep the existing certificate for now" keep_opt = "Keep the existing certificate for now"
choices = [keep_opt, choices = [keep_opt,
"Renew & replace the cert (limit ~5 per 7 days)", "Renew & replace the cert (limit ~5 per 7 days)"]
"Cancel this operation and do nothing"]
display = zope.component.getUtility(interfaces.IDisplay) display = zope.component.getUtility(interfaces.IDisplay)
response = display.menu(question, choices, "OK", "Cancel", default=0) response = display.menu(question, choices, "OK", "Cancel", default=0)
if response[0] == "cancel" or response[1] == 2: if response[0] == display_util.CANCEL:
# TODO: Add notification related to command-line options for # TODO: Add notification related to command-line options for
# skipping the menu for this case. # skipping the menu for this case.
raise errors.Error( raise errors.Error(
@@ -1973,17 +1972,6 @@ def main(cli_args=sys.argv[1:]):
zope.component.provideUtility(report) zope.component.provideUtility(report)
atexit.register(report.atexit_print_messages) atexit.register(report.atexit_print_messages)
if not os.geteuid() == 0:
logger.warning(
"Root (sudo) is required to run most of letsencrypt functionality.")
# check must be done after arg parsing as --help should work
# w/o root; on the other hand, e.g. "letsencrypt run
# --authenticator dns" or "letsencrypt plugins" does not
# require root as well
#return (
# "{0}Root is required to run letsencrypt. Please use sudo.{0}"
# .format(os.linesep))
return config.func(config, plugins) return config.func(config, plugins)
if __name__ == "__main__": if __name__ == "__main__":
+2 -2
View File
@@ -1,8 +1,8 @@
"""Let's Encrypt user-supplied configuration.""" """Let's Encrypt user-supplied configuration."""
import copy import copy
import os import os
import urlparse
from six.moves.urllib import parse # pylint: disable=import-error
import zope.interface import zope.interface
from letsencrypt import constants from letsencrypt import constants
@@ -50,7 +50,7 @@ class NamespaceConfig(object):
@property @property
def server_path(self): def server_path(self):
"""File path based on ``server``.""" """File path based on ``server``."""
parsed = urlparse.urlparse(self.namespace.server) parsed = parse.urlparse(self.namespace.server)
return (parsed.netloc + parsed.path).replace('/', os.path.sep) return (parsed.netloc + parsed.path).replace('/', os.path.sep)
@property @property
+1 -1
View File
@@ -1,11 +1,11 @@
"""Plugin common functions.""" """Plugin common functions."""
import os import os
import pkg_resources
import re import re
import shutil import shutil
import tempfile import tempfile
import OpenSSL import OpenSSL
import pkg_resources
import zope.interface import zope.interface
from acme.jose import util as jose_util from acme.jose import util as jose_util
+1 -1
View File
@@ -1,8 +1,8 @@
"""Tests for letsencrypt.plugins.disco.""" """Tests for letsencrypt.plugins.disco."""
import pkg_resources
import unittest import unittest
import mock import mock
import pkg_resources
import zope.interface import zope.interface
from letsencrypt import errors from letsencrypt import errors
+24 -13
View File
@@ -112,6 +112,21 @@ def update_configuration(lineagename, target, cli_config):
return configobj.ConfigObj(config_filename) return configobj.ConfigObj(config_filename)
def get_link_target(link):
"""Get an absolute path to the target of link.
:param str link: Path to a symbolic link
:returns: Absolute path to the target of link
:rtype: str
"""
target = os.readlink(link)
if not os.path.isabs(target):
target = os.path.join(os.path.dirname(link), target)
return os.path.abspath(target)
class RenewableCert(object): # pylint: disable=too-many-instance-attributes class RenewableCert(object): # pylint: disable=too-many-instance-attributes
"""Renewable certificate. """Renewable certificate.
@@ -194,13 +209,15 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
def _check_symlinks(self): def _check_symlinks(self):
"""Raises an exception if a symlink doesn't exist""" """Raises an exception if a symlink doesn't exist"""
def check(link):
"""Checks if symlink points to a file that exists"""
return os.path.exists(os.path.realpath(link))
for kind in ALL_FOUR: for kind in ALL_FOUR:
if not check(getattr(self, kind)): link = getattr(self, kind)
if not os.path.islink(link):
raise errors.CertStorageError( raise errors.CertStorageError(
"link: {0} does not exist".format(getattr(self, kind))) "expected {0} to be a symlink".format(link))
target = get_link_target(link)
if not os.path.exists(target):
raise errors.CertStorageError("target {0} of symlink {1} does "
"not exist".format(target, link))
def _consistent(self): def _consistent(self):
"""Are the files associated with this lineage self-consistent? """Are the files associated with this lineage self-consistent?
@@ -225,10 +242,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
return False return False
for kind in ALL_FOUR: for kind in ALL_FOUR:
link = getattr(self, kind) link = getattr(self, kind)
where = os.path.dirname(link) target = get_link_target(link)
target = os.readlink(link)
if not os.path.isabs(target):
target = os.path.join(where, target)
# Each element's link must point within the cert lineage's # Each element's link must point within the cert lineage's
# directory within the official archive directory # directory within the official archive directory
@@ -343,10 +357,7 @@ class RenewableCert(object): # pylint: disable=too-many-instance-attributes
logger.debug("Expected symlink %s for %s does not exist.", logger.debug("Expected symlink %s for %s does not exist.",
link, kind) link, kind)
return None return None
target = os.readlink(link) return get_link_target(link)
if not os.path.isabs(target):
target = os.path.join(os.path.dirname(link), target)
return os.path.abspath(target)
def current_version(self, kind): def current_version(self, kind):
"""Returns numerical version of the specified item. """Returns numerical version of the specified item.
+6 -2
View File
@@ -1,13 +1,13 @@
"""Tests for letsencrypt.storage.""" """Tests for letsencrypt.storage."""
import datetime import datetime
import pytz
import os import os
import tempfile
import shutil import shutil
import tempfile
import unittest import unittest
import configobj import configobj
import mock import mock
import pytz
from letsencrypt import configuration from letsencrypt import configuration
from letsencrypt import errors from letsencrypt import errors
@@ -687,6 +687,10 @@ class RenewableCertTests(BaseRenewableCertTest):
self.assertRaises(errors.CertStorageError, self.assertRaises(errors.CertStorageError,
storage.RenewableCert, storage.RenewableCert,
self.config.filename, self.cli_config) self.config.filename, self.cli_config)
os.symlink("missing", self.config[ALL_FOUR[0]])
self.assertRaises(errors.CertStorageError,
storage.RenewableCert,
self.config.filename, self.cli_config)
if __name__ == "__main__": if __name__ == "__main__":
-3
View File
@@ -1,9 +1,6 @@
[easy_install] [easy_install]
zip_ok = false zip_ok = false
[aliases]
dev = develop easy_install letsencrypt[dev,docs,testing]
[nosetests] [nosetests]
nocapture=1 nocapture=1
cover-package=letsencrypt,acme,letsencrypt_apache,letsencrypt_nginx cover-package=letsencrypt,acme,letsencrypt_apache,letsencrypt_nginx
+5 -9
View File
@@ -65,7 +65,12 @@ else:
dev_extras = [ dev_extras = [
# Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289 # Pin astroid==1.3.5, pylint==1.4.2 as a workaround for #289
'astroid==1.3.5', 'astroid==1.3.5',
'coverage',
'nose',
'nosexcover',
'pep8',
'pylint==1.4.2', # upstream #248 'pylint==1.4.2', # upstream #248
'tox',
'twine', 'twine',
'wheel', 'wheel',
] ]
@@ -77,14 +82,6 @@ docs_extras = [
'sphinxcontrib-programoutput', 'sphinxcontrib-programoutput',
] ]
testing_extras = [
'coverage',
'nose',
'nosexcover',
'pep8',
'tox',
]
setup( setup(
name='letsencrypt', name='letsencrypt',
version=version, version=version,
@@ -120,7 +117,6 @@ setup(
extras_require={ extras_require={
'dev': dev_extras, 'dev': dev_extras,
'docs': docs_extras, 'docs': docs_extras,
'testing': testing_extras,
}, },
# to test all packages run "python setup.py test -s # to test all packages run "python setup.py test -s
+2 -2
View File
@@ -4,8 +4,8 @@
export VENV_ARGS="--python python2" export VENV_ARGS="--python python2"
./tools/_venv_common.sh \ ./tools/_venv_common.sh \
-e acme[testing] \ -e acme[dev] \
-e .[dev,docs,testing] \ -e .[dev,docs] \
-e letsencrypt-apache \ -e letsencrypt-apache \
-e letsencrypt-nginx \ -e letsencrypt-nginx \
-e letshelp-letsencrypt \ -e letshelp-letsencrypt \
+2 -2
View File
@@ -4,5 +4,5 @@
export VENV_NAME="${VENV_NAME:-venv3}" export VENV_NAME="${VENV_NAME:-venv3}"
export VENV_ARGS="--python python3" export VENV_ARGS="--python python3"
./bootstrap/dev/_venv_common.sh \ ./tools/_venv_common.sh \
-e acme[testing] \ -e acme[dev] \
+7 -7
View File
@@ -15,9 +15,9 @@ envlist = py{26,27,33,34,35},py{26,27}-oldest,cover,lint
# packages installed separately to ensure that downstream deps problems # packages installed separately to ensure that downstream deps problems
# are detected, c.f. #1002 # are detected, c.f. #1002
commands = commands =
pip install -e acme[testing] pip install -e acme[dev]
nosetests -v acme nosetests -v acme
pip install -e .[testing] pip install -e .[dev]
nosetests -v letsencrypt nosetests -v letsencrypt
pip install -e letsencrypt-apache pip install -e letsencrypt-apache
nosetests -v letsencrypt_apache nosetests -v letsencrypt_apache
@@ -40,23 +40,23 @@ deps =
[testenv:py33] [testenv:py33]
commands = commands =
pip install -e acme[testing] pip install -e acme[dev]
nosetests -v acme nosetests -v acme
[testenv:py34] [testenv:py34]
commands = commands =
pip install -e acme[testing] pip install -e acme[dev]
nosetests -v acme nosetests -v acme
[testenv:py35] [testenv:py35]
commands = commands =
pip install -e acme[testing] pip install -e acme[dev]
nosetests -v acme nosetests -v acme
[testenv:cover] [testenv:cover]
basepython = python2.7 basepython = python2.7
commands = commands =
pip install -e acme -e .[testing] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letshelp-letsencrypt
./tox.cover.sh ./tox.cover.sh
[testenv:lint] [testenv:lint]
@@ -66,7 +66,7 @@ basepython = python2.7
# duplicate code checking; if one of the commands fails, others will # duplicate code checking; if one of the commands fails, others will
# continue, but tox return code will reflect previous error # continue, but tox return code will reflect previous error
commands = commands =
pip install -e acme -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt pip install -e acme[dev] -e .[dev] -e letsencrypt-apache -e letsencrypt-nginx -e letsencrypt-compatibility-test -e letshelp-letsencrypt
./pep8.travis.sh ./pep8.travis.sh
pylint --rcfile=.pylintrc letsencrypt pylint --rcfile=.pylintrc letsencrypt
pylint --rcfile=acme/.pylintrc acme/acme pylint --rcfile=acme/.pylintrc acme/acme