From 34a1d17ef1fc537016692c98464b06039629a94d Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 24 Nov 2015 18:19:52 -0800 Subject: [PATCH 01/57] Keep installation instructions simple and on-point - Avoid a giant red box telling people to not do something they wouldn't have thought of (if they are thinking of it, maybe we need to improve our Github landing experience)? - Move the discussion of non-recommended installation methods after all the other docs people need to read --- docs/using.rst | 177 ++++++++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 82 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index d6ae2c5ee..b8851c4c6 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -28,13 +28,8 @@ Firstly, please `install Git`_ and run the following commands: git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt -.. warning:: Alternatively you could `download the ZIP archive`_ and - extract the snapshot of our repository, but it's strongly - recommended to use the above method instead. .. _`install Git`: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git -.. _`download the ZIP archive`: - https://github.com/letsencrypt/letsencrypt/archive/master.zip To install and run the client you just need to type: @@ -61,84 +56,15 @@ or for full help, type: ./letsencrypt-auto --help all -Running with Docker -------------------- -Docker_ is an amazingly simple and quick way to obtain a -certificate. However, this mode of operation is unable to install -certificates or configure your webserver, because our installer -plugins cannot reach it from inside the Docker container. - -You should definitely read the :ref:`where-certs` section, in order to -know how to manage the certs -manually. https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance -provides some information about recommended ciphersuites. If none of -these make much sense to you, you should definitely use the -letsencrypt-auto_ method, which enables you to use installer plugins -that cover both of those hard topics. - -If you're still not convinced and have decided to use this method, -from the server that the domain you're requesting a cert for resolves -to, `install Docker`_, then issue the following command: - -.. code-block:: shell - - sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \ - -v "/etc/letsencrypt:/etc/letsencrypt" \ - -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ - quay.io/letsencrypt/letsencrypt:latest auth - -and follow the instructions (note that ``auth`` command is explicitly -used - no installer plugins involved). Your new cert will be available -in ``/etc/letsencrypt/live`` on the host. - -.. _Docker: https://docker.com -.. _`install Docker`: https://docs.docker.com/userguide/ - - -Operating System Packages --------------------------- - -**FreeBSD** - - * Port: ``cd /usr/ports/security/py-letsencrypt && make install clean`` - * Package: ``pkg install py27-letsencrypt`` - -**Arch Linux** - -.. code-block:: shell - - sudo pacman -S letsencrypt letsencrypt-apache - -**Other Operating Systems** - -Unfortunately, this is an ongoing effort. If you'd like to package -Let's Encrypt client for your distribution of choice please have a -look at the :doc:`packaging`. - - -From source ------------ - -Installation from source is only supported for developers and the -whole process is described in the :doc:`contributing`. - -.. warning:: Please do **not** use ``python setup.py install`` or - ``python pip install .``. Please do **not** attempt the - installation commands as superuser/root and/or without virtual - environment, e.g. ``sudo python setup.py install``, ``sudo pip - install``, ``sudo ./venv/bin/...``. These modes of operation might - corrupt your operating system and are **not supported** by the - Let's Encrypt team! - - -Comparison of different methods -------------------------------- - -Unless you have a very specific requirements, we kindly ask you to use -the letsencrypt-auto_ method. It's the fastest, the most thoroughly -tested and the most reliable way of getting our software and the free -SSL certificates! +``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 +experimental, Arch linux and FreeBSD now have native packages, so on those +systems you can just install ``letsencrypt`` (and perhaps +``letsencrypt-apache``). If you'd like to run the latest copy from Git, or +run your own locally modified copy of the client, read the developer docs on +:doc:`contributing`. Some `other methods of installation`_ are discussed +below. Plugins @@ -352,6 +278,93 @@ give us us as much information as possible: - your operating system, including specific version - specify which installation_ method you've chosen +Other methods of installation +============================= + +Running with Docker +------------------- + +Docker_ is an amazingly simple and quick way to obtain a +certificate. However, this mode of operation is unable to install +certificates or configure your webserver, because our installer +plugins cannot reach it from inside the Docker container. + +You should definitely read the :ref:`where-certs` section, in order to +know how to manage the certs +manually. https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance +provides some information about recommended ciphersuites. If none of +these make much sense to you, you should definitely use the +letsencrypt-auto_ method, which enables you to use installer plugins +that cover both of those hard topics. + +If you're still not convinced and have decided to use this method, +from the server that the domain you're requesting a cert for resolves +to, `install Docker`_, then issue the following command: + +.. code-block:: shell + + sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \ + -v "/etc/letsencrypt:/etc/letsencrypt" \ + -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ + quay.io/letsencrypt/letsencrypt:latest auth + +and follow the instructions (note that ``auth`` command is explicitly +used - no installer plugins involved). Your new cert will be available +in ``/etc/letsencrypt/live`` on the host. + +.. _Docker: https://docker.com +.. _`install Docker`: https://docs.docker.com/userguide/ + + +Operating System Packages +-------------------------- + +**FreeBSD** + + * Port: ``cd /usr/ports/security/py-letsencrypt && make install clean`` + * Package: ``pkg install py27-letsencrypt`` + +**Arch Linux** + +.. code-block:: shell + + sudo pacman -S letsencrypt letsencrypt-apache + +**Other Operating Systems** + +Unfortunately, this is an ongoing effort. If you'd like to package +Let's Encrypt client for your distribution of choice please have a +look at the :doc:`packaging`. + + +From source +----------- + +Installation from source is only supported for developers and the +whole process is described in the :doc:`contributing`. + +.. warning:: Please do **not** use ``python setup.py install`` or + ``python pip install .``. Please do **not** attempt the + installation commands as superuser/root and/or without virtual + environment, e.g. ``sudo python setup.py install``, ``sudo pip + install``, ``sudo ./venv/bin/...``. These modes of operation might + corrupt your operating system and are **not supported** by the + Let's Encrypt team! + + +Comparison of different methods +------------------------------- + +Unless you have a very specific requirements, we kindly ask you to use +the letsencrypt-auto_ method. It's the fastest, the most thoroughly +tested and the most reliable way of getting our software and the free +SSL certificates! + +Beyond the methods discussed here, other methods may be possible, such as +installing Let's Encrypt directly with pip from PyPI or downloading a ZIP +archive from GitHub may be technically possible but are not presently +supported. + .. rubric:: Footnotes From d4542d607ef3f020b7e7c8bbfcf660d5091476ea Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 25 Nov 2015 12:59:16 -0800 Subject: [PATCH 02/57] Update plugin docs, especially webroot for -w --- docs/using.rst | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index b8851c4c6..892f10ac8 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -70,6 +70,9 @@ below. Plugins ======= +The Let's Encrypt client supports a number of different "plugins" that can be +used to obtain and/or install certificates. + =========== = = =============================================================== Plugin A I Notes =========== = = =============================================================== @@ -87,7 +90,7 @@ Apache If you're running Apache 2.4 on a Debian-based OS with version 1.0+ of the ``libaugeas0`` package available, you can use the Apache plugin. -This automates both obtaining and installing certs on an Apache +This automates both obtaining *and* installing certs on an Apache webserver. To specify this plugin on the command line, simply include ``--apache``. @@ -110,13 +113,22 @@ Webroot If you're running a webserver that you don't want to stop to use 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`` with the root +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 multiple domains are specified, they must all use the same path. -Additionally, your server must be configured to serve files from -hidden directories. + +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. + +Note that to use the webroot plugin, your server must be configured to serve +files from hidden directories. Manual ------ @@ -363,7 +375,7 @@ SSL certificates! Beyond the methods discussed here, other methods may be possible, such as installing Let's Encrypt directly with pip from PyPI or downloading a ZIP archive from GitHub may be technically possible but are not presently -supported. +recommended or supported. .. rubric:: Footnotes From cc29037b67256b6f85f051f99775102288874315 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 25 Nov 2015 13:20:41 -0800 Subject: [PATCH 03/57] Document debian experimental packages --- docs/using.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 892f10ac8..aca896281 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -62,8 +62,8 @@ client beta releases on systems that don't have a packaged version. Debian experimental, Arch linux and FreeBSD now have native packages, so on those systems you can just install ``letsencrypt`` (and perhaps ``letsencrypt-apache``). If you'd like to run the latest copy from Git, or -run your own locally modified copy of the client, read the developer docs on -:doc:`contributing`. Some `other methods of installation`_ are discussed +run your own locally modified copy of the client, follow the instructions in +the :doc:`contributing`. Some `other methods of installation`_ are discussed below. @@ -342,9 +342,23 @@ Operating System Packages sudo pacman -S letsencrypt letsencrypt-apache +**Debian Experimental** + +If you run Debian unstable, you can install experimental letsencrypt packages. +Add the line ``deb http://ftp.us.debian.org/debian/ experimental main`` (or +the equivalent for your country) to ``/etc/apt/sources.list``, then run + +.. code-block:: shell + + sudo apt-get update + sudo apt-get -t experimental install letsencrypt python-letsencrypt-apache + +If you don't want to use the Apache plugin, you can ommit the +``python-letsencrypt-apache`` package. + **Other Operating Systems** -Unfortunately, this is an ongoing effort. If you'd like to package +OS packaging is an ongoing effort. If you'd like to package Let's Encrypt client for your distribution of choice please have a look at the :doc:`packaging`. From 9d5500e7bb467eb15ccf92288a99005c29ae5470 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 25 Nov 2015 15:29:52 -0800 Subject: [PATCH 04/57] Plugin docs: Improve table, explain authenticators & installers --- docs/using.rst | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index aca896281..b546e3005 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -71,19 +71,26 @@ Plugins ======= The Let's Encrypt client supports a number of different "plugins" that can be -used to obtain and/or install certificates. +used to obtain and/or install certificates. Plugins that can obtain a cert +are called "authenticators" and can be used with the "certonly" command. +Plugins that can install a cert are called "installers". Plugins that do both +can be used with the "letsencrypt run" command, which is the default. -=========== = = =============================================================== -Plugin A I Notes -=========== = = =============================================================== -apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on - Debian-based distributions with ``libaugeas0`` 1.0+. -standalone_ Y N Uses a "standalone" webserver to obtain a cert. -webroot_ Y N Obtains a cert using an already running webserver. -manual_ Y N Helps you obtain a cert by giving you instructions to perform - domain validation yourself. -nginx_ Y Y Very experimental and not included in letsencrypt-auto_. -=========== = = =============================================================== +=========== ==== ==== =============================================================== +Plugin Auth Inst Notes +=========== ==== ==== =============================================================== +apache_ Y Y Automates obtaining and installing a cert with Apache 2.4 on + Debian-based distributions with ``libaugeas0`` 1.0+. +standalone_ Y N Uses a "standalone" webserver to obtain a cert. +webroot_ Y N Obtains a cert by writing to the webroot directory of an + already running webserver. +manual_ Y N Helps you obtain a cert by giving you instructions to perform + domain validation yourself. +nginx_ Y Y Very experimental and not included in letsencrypt-auto_. +=========== ==== ==== =============================================================== + +Future plugins for IMAP servers, SMTP servers, IRC servers, etc, are likely to +be installers but not authenticators. Apache ------ From d52d995a2c859e422a2d39feacac22807f27fd6f Mon Sep 17 00:00:00 2001 From: TheNavigat Date: Fri, 27 Nov 2015 13:30:41 +0200 Subject: [PATCH 05/57] Adding Python 2.6 and 2.7 note to README file --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index ce0d1b686..509b2ae43 100644 --- a/README.rst +++ b/README.rst @@ -47,6 +47,11 @@ server automatically!:: user@www:~$ sudo letsencrypt -d www.example.org run +Let's Encrypt supports Python 2.6 and 2.7 only. Check +Python_'s website for instructions on how to install Python on your +computer. + +.. _Python: https://wiki.python.org/moin/BeginnersGuide/Download **Encrypt ALL the things!** From de477f1f69ee1f2c7e260aac9343be25c5fec197 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 1 Dec 2015 14:05:32 -0800 Subject: [PATCH 06/57] Change default server --- letsencrypt/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/constants.py b/letsencrypt/constants.py index a402ce923..40155abd7 100644 --- a/letsencrypt/constants.py +++ b/letsencrypt/constants.py @@ -16,7 +16,7 @@ CLI_DEFAULTS = dict( "letsencrypt", "cli.ini"), ], verbose_count=-(logging.WARNING / 10), - server="https://acme-staging.api.letsencrypt.org/directory", + server="https://acme-v01.api.letsencrypt.org/directory", rsa_key_size=2048, rollback_checkpoints=1, config_dir="/etc/letsencrypt", From 87d3cebab2dc65cc6043d904ed21b71b070d6ead Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 1 Dec 2015 16:33:15 -0800 Subject: [PATCH 07/57] Make Helpful more helpful --- letsencrypt/cli.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 2fae5fe3e..454f4897f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -771,6 +771,20 @@ class HelpfulArgumentParser(object): kwargs["help"] = argparse.SUPPRESS self.parser.add_argument(*args, **kwargs) + def add_deprecated_argument(self, argument_name, num_args): + """Adds a deprecated argument with the name argument_name. + + Deprecated arguments are not shown in the help. If they are used + on the command line, a warning is shown stating that the + argument is deprecated and no other action is taken. + + :param str argument_name: Name of deprecated argument. + :param int nargs: Number of arguments the option takes. + + """ + le_util.add_deprecated_argument( + self.parser.add_argument, argument_name, num_args) + def add_group(self, topic, **kwargs): """ From 462139fca9d7358df0ca0f24085ae0afddd3cbb9 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Tue, 1 Dec 2015 16:51:05 -0800 Subject: [PATCH 08/57] Kill --agree-dev-preview --- DISCLAIMER | 1 - Dockerfile-dev | 2 +- MANIFEST.in | 1 - examples/dev-cli.ini | 1 - letsencrypt/DISCLAIMER | 5 ----- letsencrypt/cli.py | 13 ++----------- letsencrypt/tests/cli_test.py | 9 ++++----- tests/integration/_common.sh | 1 - 8 files changed, 7 insertions(+), 26 deletions(-) delete mode 120000 DISCLAIMER delete mode 100644 letsencrypt/DISCLAIMER diff --git a/DISCLAIMER b/DISCLAIMER deleted file mode 120000 index e580554ff..000000000 --- a/DISCLAIMER +++ /dev/null @@ -1 +0,0 @@ -letsencrypt/DISCLAIMER \ No newline at end of file diff --git a/Dockerfile-dev b/Dockerfile-dev index 838b60e8b..b89411c90 100644 --- a/Dockerfile-dev +++ b/Dockerfile-dev @@ -33,7 +33,7 @@ RUN /opt/letsencrypt/src/ubuntu.sh && \ # Dockerfile we make sure we cache as much as possible # py26reqs.txt not installed! -COPY setup.py README.rst CHANGES.rst MANIFEST.in DISCLAIMER linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/ +COPY setup.py README.rst CHANGES.rst MANIFEST.in linter_plugin.py tox.cover.sh tox.ini pep8.travis.sh .pep8 .pylintrc /opt/letsencrypt/src/ # all above files are necessary for setup.py, however, package source # code directory has to be copied separately to a subdirectory... diff --git a/MANIFEST.in b/MANIFEST.in index 5d5b0bed4..a82c7dd8c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,7 +4,6 @@ include CHANGES.rst include CONTRIBUTING.md include LICENSE.txt include linter_plugin.py -include letsencrypt/DISCLAIMER recursive-include docs * recursive-include examples * recursive-include letsencrypt/tests/testdata * diff --git a/examples/dev-cli.ini b/examples/dev-cli.ini index 2ea5d247d..be703814a 100644 --- a/examples/dev-cli.ini +++ b/examples/dev-cli.ini @@ -8,7 +8,6 @@ email = foo@example.com domains = example.com text = True -agree-dev-preview = True agree-tos = True debug = True # Unfortunately, it's not possible to specify "verbose" multiple times diff --git a/letsencrypt/DISCLAIMER b/letsencrypt/DISCLAIMER deleted file mode 100644 index dd7759361..000000000 --- a/letsencrypt/DISCLAIMER +++ /dev/null @@ -1,5 +0,0 @@ -This is a PREVIEW RELEASE of a client application for the Let's Encrypt certificate authority and other services using the ACME protocol. The Let's Encrypt certificate authority is NOT YET ISSUING CERTIFICATES TO THE PUBLIC. - -Until publicly-trusted certificates can be issued by Let's Encrypt, this software CANNOT OBTAIN A PUBLICLY-TRUSTED CERTIFICATE FOR YOUR WEB SERVER. You should only use this program if you are a developer interested in experimenting with the ACME protocol or in helping to improve this software. If you want to configure your web site with HTTPS in the meantime, please obtain a certificate from a different authority. - -For updates on the status of Let's Encrypt, please visit the Let's Encrypt home page at https://letsencrypt.org/. diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 454f4897f..fd53316b1 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -6,7 +6,6 @@ import functools import logging import logging.handlers import os -import pkg_resources import sys import time import traceback @@ -894,9 +893,6 @@ def prepare_and_parse_args(plugins, args): "automation", "--renew-by-default", action="store_true", help="Select renewal by default when domains are a superset of a " "a previously attained cert") - helpful.add( - "automation", "--agree-dev-preview", action="store_true", - help="Agree to the Let's Encrypt Developer Preview Disclaimer") helpful.add( "automation", "--agree-tos", dest="tos", action="store_true", help="Agree to the Let's Encrypt Subscriber Agreement") @@ -961,6 +957,8 @@ def prepare_and_parse_args(plugins, args): help="Require that all configuration files are owned by the current " "user; only needed if your config is somewhere unsafe like /tmp/") + helpful.add_deprecated_argument("--agree-dev-preview", 0) + _create_subparsers(helpful) _paths_parser(helpful) # _plugins_parsing should be the last thing to act upon the main @@ -1218,13 +1216,6 @@ def main(cli_args=sys.argv[1:]): zope.component.provideUtility(report) atexit.register(report.atexit_print_messages) - # TODO: remove developer preview prompt for the launch - if not config.agree_dev_preview: - disclaimer = pkg_resources.resource_string("letsencrypt", "DISCLAIMER") - if not zope.component.getUtility(interfaces.IDisplay).yesno( - disclaimer, "Agree", "Cancel"): - raise errors.Error("Must agree to TOS") - if not os.geteuid() == 0: logger.warning( "Root (sudo) is required to run most of letsencrypt functionality.") diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index e512668c5..36f8c4fc7 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -39,9 +39,9 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.config_dir = os.path.join(self.tmp_dir, 'config') self.work_dir = os.path.join(self.tmp_dir, 'work') self.logs_dir = os.path.join(self.tmp_dir, 'logs') - self.standard_args = ['--text', '--config-dir', self.config_dir, - '--work-dir', self.work_dir, '--logs-dir', - self.logs_dir, '--agree-dev-preview'] + self.standard_args = ['--config-dir', self.config_dir, + '--work-dir', self.work_dir, + '--logs-dir', self.logs_dir, '--text'] def tearDown(self): shutil.rmtree(self.tmp_dir) @@ -180,8 +180,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def test_configurator_selection(self, mock_exe_exists): mock_exe_exists.return_value = True real_plugins = disco.PluginsRegistry.find_all() - args = ['--agree-dev-preview', '--apache', - '--authenticator', 'standalone'] + args = ['--apache', '--authenticator', 'standalone'] # This needed two calls to find_all(), which we're avoiding for now # because of possible side effects: diff --git a/tests/integration/_common.sh b/tests/integration/_common.sh index 71d745d93..4572b0fb3 100755 --- a/tests/integration/_common.sh +++ b/tests/integration/_common.sh @@ -21,7 +21,6 @@ letsencrypt_test () { $store_flags \ --text \ --no-redirect \ - --agree-dev-preview \ --agree-tos \ --register-unsafely-without-email \ --renew-by-default \ From b86abf654722ef9da536d83497967aa6d0567c5b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Tue, 1 Dec 2015 19:08:04 -0800 Subject: [PATCH 09/57] Include root as a system requirement; recommend letsencrypt-nosudo & simp_le. --- README.rst | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 4afbd2a20..018b343fd 100644 --- a/README.rst +++ b/README.rst @@ -121,12 +121,20 @@ email to client-dev+subscribe@letsencrypt.org) System Requirements =================== -The Let's Encrypt client presently only runs on Unix-ish OSes that include +The Let's Encrypt Client presently only runs on Unix-ish OSes that include Python 2.6 or 2.7; Python 3.x support will be added after the Public Beta -launch. +launch. The client requires root access in order to write to +``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to +bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and +modify webserver configurations (if you use the ``apache`` or ``nginx`` +plugins). If none of these apply to you, it is theoretically possible to run +without root privilegess, but for most users who want to avoid running an ACME +client as root, either `letsencrypt-nosudo +`_ or `simp_le +`_ are more appropriate choices. -The Apache plugin requires a debian-based OS with augeas version 1.0 or -higher. +The Apache plugin currently requires a Debian-based OS with augeas version +1.0; this includes Ubuntu 12.04+ and Debian 7+. Current Features From dca044d330d7e128ac7b71663a9574d8712e77fe Mon Sep 17 00:00:00 2001 From: Christian Rosentreter Date: Wed, 2 Dec 2015 17:16:23 +0100 Subject: [PATCH 10/57] Fixed some spelling errors. --- letsencrypt/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 0498d66c4..8d3143f61 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -858,7 +858,7 @@ def prepare_and_parse_args(plugins, args): helpful.add( "automation", "--renew-by-default", action="store_true", help="Select renewal by default when domains are a superset of a " - "a previously attained cert") + "previously attained cert") helpful.add( "automation", "--agree-dev-preview", action="store_true", help="Agree to the Let's Encrypt Developer Preview Disclaimer") @@ -1055,7 +1055,7 @@ def _plugins_parsing(helpful, plugins): helpful.add("webroot", "-w", "--webroot-path", action=WebrootPathProcessor, help="public_html / webroot path. This can be specified multiple times to " "handle different domains; each domain will have the webroot path that" - " precededed it. For instance: `-w /var/www/example -d example.com -d " + " preceded it. For instance: `-w /var/www/example -d example.com -d " "www.example.com -w /var/www/thing -d thing.net -d m.thing.net`") parse_dict = lambda s: dict(json.loads(s)) # --webroot-map still has some awkward properties, so it is undocumented From 1f7d34cde228e21088d1f29683d064956c80c43b Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 10:52:36 -0800 Subject: [PATCH 11/57] Add some suggested donation links upon success --- letsencrypt/cli.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 0498d66c4..2b3401c19 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -304,6 +304,19 @@ def _report_new_cert(cert_path, fullchain_path): .format(and_chain, path, expiry)) reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) +def _suggest_donate(): + """Reports the creation of a new certificate to the user. + + :param str cert_path: path to cert + :param str fullchain_path: path to full chain + + """ + reporter_util = zope.component.getUtility(interfaces.IReporter) + msg = ("If like Let's Encrypt, please consider supporting our work by:\n\n" + "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" + "Donating to EFF: https://eff.org/donate-le\n\n") + reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) + def _auth_from_domains(le_client, config, domains): """Authenticate and enroll certificate.""" @@ -473,6 +486,8 @@ def run(args, config, plugins): # pylint: disable=too-many-branches,too-many-lo else: display_ops.success_renewal(domains) + _suggest_donate() + def obtain_cert(args, config, plugins): """Authenticate & obtain cert, but do not install it.""" @@ -502,6 +517,8 @@ def obtain_cert(args, config, plugins): domains = _find_domains(args, installer) _auth_from_domains(le_client, config, domains) + _suggest_donate() + def install(args, config, plugins): """Install a previously obtained cert in a server.""" From e9a53c8ceec4eda2b8a08435cf2bb17a5088efa2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 12:48:10 -0800 Subject: [PATCH 12/57] Fix test cases - That call took a lot of mocking, I don't yet understand why _report_new_cert didn't require comparable treatment... --- letsencrypt/tests/cli_test.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index e0fd145e4..15c14d6e0 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -48,16 +48,18 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods def _call(self, args): "Run the cli with output streams and actual client mocked out" - with mock.patch('letsencrypt.cli.client') as client: - ret, stdout, stderr = self._call_no_clientmock(args) - return ret, stdout, stderr, client + with mock.patch('letsencrypt.cli._suggest_donate'): + with mock.patch('letsencrypt.cli.client') as client: + ret, stdout, stderr = self._call_no_clientmock(args) + return ret, stdout, stderr, client def _call_no_clientmock(self, args): "Run the client with output streams mocked out" args = self.standard_args + args - with mock.patch('letsencrypt.cli.sys.stdout') as stdout: - with mock.patch('letsencrypt.cli.sys.stderr') as stderr: - ret = cli.main(args[:]) # NOTE: parser can alter its args! + with mock.patch('letsencrypt.cli._suggest_donate'): + with mock.patch('letsencrypt.cli.sys.stdout') as stdout: + with mock.patch('letsencrypt.cli.sys.stderr') as stderr: + ret = cli.main(args[:]) # NOTE: parser can alter its args! return ret, stdout, stderr def _call_stdout(self, args): @@ -66,9 +68,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods caller. """ args = self.standard_args + args - with mock.patch('letsencrypt.cli.sys.stderr') as stderr: - with mock.patch('letsencrypt.cli.client') as client: - ret = cli.main(args[:]) # NOTE: parser can alter its args! + with mock.patch('letsencrypt.cli._suggest_donate'): + with mock.patch('letsencrypt.cli.sys.stderr') as stderr: + with mock.patch('letsencrypt.cli.client') as client: + ret = cli.main(args[:]) # NOTE: parser can alter its args! return ret, None, stderr, client def test_no_flags(self): @@ -360,9 +363,10 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods namespace = cli.prepare_and_parse_args(plugins, webroot_map_args) self.assertEqual(namespace.webroot_map, {u"eg.com": u"/tmp"}) + @mock.patch('letsencrypt.cli._suggest_donate') @mock.patch('letsencrypt.crypto_util.notAfter') @mock.patch('letsencrypt.cli.zope.component.getUtility') - def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter): + def test_certonly_new_request_success(self, mock_get_utility, mock_notAfter, _suggest): cert_path = '/etc/letsencrypt/live/foo.bar' date = '1970-01-01' mock_notAfter().date.return_value = date @@ -391,10 +395,11 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_init.return_value = mock_client self._call(['-d', 'foo.bar', '-a', 'standalone', 'certonly']) + @mock.patch('letsencrypt.cli._suggest_donate') @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._treat_as_renewal') @mock.patch('letsencrypt.cli._init_le_client') - def test_certonly_renewal(self, mock_init, mock_renewal, mock_get_utility): + def test_certonly_renewal(self, mock_init, mock_renewal, mock_get_utility, suggest): cert_path = '/etc/letsencrypt/live/foo.bar/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' @@ -416,13 +421,14 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue( chain_path in mock_get_utility().add_message.call_args[0][0]) + @mock.patch('letsencrypt.cli._suggest_donate') @mock.patch('letsencrypt.crypto_util.notAfter') @mock.patch('letsencrypt.cli.display_ops.pick_installer') @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._init_le_client') @mock.patch('letsencrypt.cli.record_chosen_plugins') def test_certonly_csr(self, _rec, mock_init, mock_get_utility, - mock_pick_installer, mock_notAfter): + mock_pick_installer, mock_notAfter, _suggest): cert_path = '/etc/letsencrypt/live/blahcert.pem' date = '1970-01-01' mock_notAfter().date.return_value = date From 35093e8e3da7b68d6eacbfb52690093fe64422eb Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 13:08:58 -0800 Subject: [PATCH 13/57] Unmocking _suggest_donate will be tricky, so reduce coverage for now --- tox.cover.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.cover.sh b/tox.cover.sh index 8418de9a8..edfd9b81a 100755 --- a/tox.cover.sh +++ b/tox.cover.sh @@ -16,7 +16,7 @@ fi cover () { if [ "$1" = "letsencrypt" ]; then - min=98 + min=97 elif [ "$1" = "acme" ]; then min=100 elif [ "$1" = "letsencrypt_apache" ]; then From 96d31aea008bfc0f41edd4ef3fda3f02610a5ceb Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 13:09:22 -0800 Subject: [PATCH 14/57] Correctly document _suggest_donate --- letsencrypt/cli.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 2b3401c19..3295e7831 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -305,12 +305,7 @@ def _report_new_cert(cert_path, fullchain_path): reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) def _suggest_donate(): - """Reports the creation of a new certificate to the user. - - :param str cert_path: path to cert - :param str fullchain_path: path to full chain - - """ + "Suggest a donation to support Let's Encrypt" reporter_util = zope.component.getUtility(interfaces.IReporter) msg = ("If like Let's Encrypt, please consider supporting our work by:\n\n" "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" From bbfb33b7055d2b4efd1c44569d77f346e6a08c34 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 14:20:53 -0800 Subject: [PATCH 15/57] missing underscore was breaking lint, but only under cover? --- letsencrypt/tests/cli_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/tests/cli_test.py b/letsencrypt/tests/cli_test.py index 15c14d6e0..8ac34dd2a 100644 --- a/letsencrypt/tests/cli_test.py +++ b/letsencrypt/tests/cli_test.py @@ -399,7 +399,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods @mock.patch('letsencrypt.cli.zope.component.getUtility') @mock.patch('letsencrypt.cli._treat_as_renewal') @mock.patch('letsencrypt.cli._init_le_client') - def test_certonly_renewal(self, mock_init, mock_renewal, mock_get_utility, suggest): + def test_certonly_renewal(self, mock_init, mock_renewal, mock_get_utility, _suggest): cert_path = '/etc/letsencrypt/live/foo.bar/cert.pem' chain_path = '/etc/letsencrypt/live/foo.bar/fullchain.pem' From df51f7f50c0737b1453ae293ab509e0eb12eda42 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 15:15:08 -0800 Subject: [PATCH 16/57] Version 0.1.0 for Public Beta! --- letsencrypt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index 1155a5b0c..a2cc7d31a 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.1.0.dev0' +__version__ = '0.1.0' From 5747ab7fd9641986833bad474d71b46a8c589247 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 15:55:43 -0800 Subject: [PATCH 17/57] Release 0.1.0 --- acme/setup.py | 2 +- letsencrypt-apache/setup.py | 2 +- letsencrypt-nginx/setup.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index a6551a023..1889ec020 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0.dev0' +version = '0.1.0' install_requires = [ # load_pem_private/public_key (>=0.6) diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index e4dd11935..3b994c0ef 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0.dev0' +version = '0.1.0' install_requires = [ 'acme=={0}'.format(version), diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index a669ad841..93986c10e 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0.dev0' +version = '0.1.0' install_requires = [ 'acme=={0}'.format(version), diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index 04b879e14..a5a069c55 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0.dev0' +version = '0.1.0' install_requires = [ 'setuptools', # pkg_resources From 047ae326f6f901fd2c1bc1ab448248da992018c1 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Wed, 2 Dec 2015 19:04:31 -0800 Subject: [PATCH 18/57] Bump anticipated release version --- letsencrypt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index a2cc7d31a..e011c3f9b 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.1.0' +__version__ = '0.1.1' From 54c74a6d2fa6cc134676758142075db43f738b2a Mon Sep 17 00:00:00 2001 From: Anselm Levskaya Date: Wed, 2 Dec 2015 23:06:11 -0800 Subject: [PATCH 19/57] fix sudo issue on amazon linux instance with letsencrypt-auto the letsencrypt-auto script was missing the sudo parameter on call to ExperimentalBootstrap for amazon linux. also added comment to mac entry to clarify why it lacks the parameter --- letsencrypt-auto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt-auto b/letsencrypt-auto index c88028b72..44c71883c 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -147,9 +147,9 @@ then elif uname | grep -iq FreeBSD ; then ExperimentalBootstrap "FreeBSD" freebsd.sh "$SUDO" elif uname | grep -iq Darwin ; then - ExperimentalBootstrap "Mac OS X" mac.sh + ExperimentalBootstrap "Mac OS X" mac.sh # homebrew doesn't normally run as root elif grep -iq "Amazon Linux" /etc/issue ; then - ExperimentalBootstrap "Amazon Linux" _rpm_common.sh + ExperimentalBootstrap "Amazon Linux" _rpm_common.sh "$SUDO" else echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" echo From c7dbf8aa24ca08cc977b5bdef3003eeeee3513aa Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Wed, 2 Dec 2015 16:12:47 +0200 Subject: [PATCH 20/57] Avoid trailing whitespace in pretty-printed JSON Fixes a failing test on Python 3.3: ====================================================================== FAIL: test_json_dumps_pretty (acme.jose.interfaces_test.JSONDeSerializableTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/mg/src/letsencrypt/acme/acme/jose/interfaces_test.py", line 97, in test_json_dumps_pretty '[\n "foo1",{0}\n "foo2"\n]'.format(filler)) AssertionError: '[\n "foo1", \n "foo2"\n]' != '[\n "foo1",\n "foo2"\n]' [ - "foo1", ? - + "foo1", "foo2" ] ---------------------------------------------------------------------- (The test expected trailing whitespace on Python < 3.0, while it should've been checking for Python < 3.4.) --- acme/acme/jose/interfaces.py | 2 +- acme/acme/jose/interfaces_test.py | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/acme/acme/jose/interfaces.py b/acme/acme/jose/interfaces.py index f85777a30..f841848b3 100644 --- a/acme/acme/jose/interfaces.py +++ b/acme/acme/jose/interfaces.py @@ -194,7 +194,7 @@ class JSONDeSerializable(object): :rtype: str """ - return self.json_dumps(sort_keys=True, indent=4) + return self.json_dumps(sort_keys=True, indent=4, separators=(',', ': ')) @classmethod def json_dump_default(cls, python_object): diff --git a/acme/acme/jose/interfaces_test.py b/acme/acme/jose/interfaces_test.py index 84dc2a1be..cf98ff371 100644 --- a/acme/acme/jose/interfaces_test.py +++ b/acme/acme/jose/interfaces_test.py @@ -1,8 +1,6 @@ """Tests for acme.jose.interfaces.""" import unittest -import six - class JSONDeSerializableTest(unittest.TestCase): # pylint: disable=too-many-instance-attributes @@ -92,9 +90,8 @@ class JSONDeSerializableTest(unittest.TestCase): self.assertEqual('["foo1", "foo2"]', self.seq.json_dumps()) def test_json_dumps_pretty(self): - filler = ' ' if six.PY2 else '' self.assertEqual(self.seq.json_dumps_pretty(), - '[\n "foo1",{0}\n "foo2"\n]'.format(filler)) + '[\n "foo1",\n "foo2"\n]') def test_json_dump_default(self): from acme.jose.interfaces import JSONDeSerializable From 7a6e084e3ab4ae82c55acd9535d1a758985e96ec Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Thu, 3 Dec 2015 15:55:17 +0000 Subject: [PATCH 21/57] Unbreak master --- acme/setup.py | 2 +- letsencrypt-apache/setup.py | 2 +- letsencrypt-compatibility-test/setup.py | 2 +- letsencrypt-nginx/setup.py | 2 +- letsencrypt/__init__.py | 2 +- letshelp-letsencrypt/setup.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/acme/setup.py b/acme/setup.py index 1889ec020..e35b40d6e 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0' +version = '0.2.0.dev0' install_requires = [ # load_pem_private/public_key (>=0.6) diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index 3b994c0ef..58008e1e4 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0' +version = '0.2.0.dev0' install_requires = [ 'acme=={0}'.format(version), diff --git a/letsencrypt-compatibility-test/setup.py b/letsencrypt-compatibility-test/setup.py index c791d51c4..eb7e23036 100644 --- a/letsencrypt-compatibility-test/setup.py +++ b/letsencrypt-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0.dev0' +version = '0.2.0.dev0' install_requires = [ 'letsencrypt=={0}'.format(version), diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index 93986c10e..1d42fe488 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0' +version = '0.2.0.dev0' install_requires = [ 'acme=={0}'.format(version), diff --git a/letsencrypt/__init__.py b/letsencrypt/__init__.py index e011c3f9b..1c7815f78 100644 --- a/letsencrypt/__init__.py +++ b/letsencrypt/__init__.py @@ -1,4 +1,4 @@ """Let's Encrypt client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.1.1' +__version__ = '0.2.0.dev0' diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index a5a069c55..d487e556d 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.1.0' +version = '0.2.0.dev0' install_requires = [ 'setuptools', # pkg_resources From 0004610e610b3491a190b074ddaa20e592ef3d93 Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Thu, 3 Dec 2015 12:40:10 -0800 Subject: [PATCH 22/57] Fixing the grammar of the _suggest_donate message --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 9835fa126..3652f828f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -306,7 +306,7 @@ def _report_new_cert(cert_path, fullchain_path): def _suggest_donate(): "Suggest a donation to support Let's Encrypt" reporter_util = zope.component.getUtility(interfaces.IReporter) - msg = ("If like Let's Encrypt, please consider supporting our work by:\n\n" + msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n" "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" "Donating to EFF: https://eff.org/donate-le\n\n") reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) From ad5352e8cced72fd6f208eea4e96575bb60bf4e2 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 3 Dec 2015 12:54:32 -0800 Subject: [PATCH 23/57] Upstream augeas fix for backslashes at the start of directive args From: https://github.com/hercules-team/augeas/pull/325 Fixes: #1531 --- letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug index 30d8ca501..83d97f7a4 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug +++ b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug @@ -59,7 +59,7 @@ let empty = Util.empty_dos let indent = Util.indent (* borrowed from shellvars.aug *) -let char_arg_dir = /([^\\ '"\t\r\n]|[^\\ '"\t\r\n][^ '"\t\r\n]*[^\\ '"\t\r\n])|\\\\"|\\\\'/ +let char_arg_dir = /([^\\ '"\t\r\n]|[^ '"\t\r\n]+[^\\ '"\t\r\n])|\\\\"|\\\\'/ let char_arg_sec = /[^ '"\t\r\n>]|\\\\"|\\\\'/ let cdot = /\\\\./ let cl = /\\\\\n/ From 55d51530d916acc8b418c7b79c3bd8d03d56fa2a Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 3 Dec 2015 12:56:29 -0800 Subject: [PATCH 24/57] This should fix parsing of drupal .htaccess files --- .../{failing => passing}/drupal-htaccess-1531.conf | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/apache-conf-files/{failing => passing}/drupal-htaccess-1531.conf (100%) diff --git a/tests/apache-conf-files/failing/drupal-htaccess-1531.conf b/tests/apache-conf-files/passing/drupal-htaccess-1531.conf similarity index 100% rename from tests/apache-conf-files/failing/drupal-htaccess-1531.conf rename to tests/apache-conf-files/passing/drupal-htaccess-1531.conf From 5a39e833c4982085ed88562dd08050966bf7590e Mon Sep 17 00:00:00 2001 From: Alex Conlin Date: Thu, 3 Dec 2015 22:14:43 +0000 Subject: [PATCH 25/57] Fix typo in README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 018b343fd..f25dc1956 100644 --- a/README.rst +++ b/README.rst @@ -128,7 +128,7 @@ launch. The client requires root access in order to write to bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and modify webserver configurations (if you use the ``apache`` or ``nginx`` plugins). If none of these apply to you, it is theoretically possible to run -without root privilegess, but for most users who want to avoid running an ACME +without root privileges, but for most users who want to avoid running an ACME client as root, either `letsencrypt-nosudo `_ or `simp_le `_ are more appropriate choices. From 3add88c64173b6b551018c9939e89a9153c39955 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 3 Dec 2015 15:25:54 -0800 Subject: [PATCH 26/57] Add another apache conf test case --- .../failing/two-blocks-one-line-1693.conf | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/apache-conf-files/failing/two-blocks-one-line-1693.conf diff --git a/tests/apache-conf-files/failing/two-blocks-one-line-1693.conf b/tests/apache-conf-files/failing/two-blocks-one-line-1693.conf new file mode 100644 index 000000000..5d3cef423 --- /dev/null +++ b/tests/apache-conf-files/failing/two-blocks-one-line-1693.conf @@ -0,0 +1,28 @@ + + + ServerAdmin info@somethingnewentertainment.com + ServerName somethingnewentertainment.com + DocumentRoot /var/www/html + + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined + + SSLEngine on + SSLProtocol all -SSLv2 -SSLv3 + SSLHonorCipherOrder on + SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EEC DH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRS A RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4" + + SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem + SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key + + + SSLOptions +StdEnvVars + + + SSLOptions +StdEnvVars + + BrowserMatch "MSIE [2-6]" \ + nokeepalive ssl-unclean-shutdown \ + downgrade-1.0 force-response-1.0 + BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown + From a4396b89a7ab89635845816d4d05cddccfb963df Mon Sep 17 00:00:00 2001 From: j Date: Thu, 3 Dec 2015 19:14:21 +0100 Subject: [PATCH 27/57] Remove ! at end of url (fixes open url in gnome-terminal) The ! at the end of the url is parsed as part of the url if one uses "Open Link" in gnome-terminal. --- letsencrypt/display/ops.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index 038ad6fdc..5c8c543b0 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -245,7 +245,7 @@ def success_installation(domains): """ util(interfaces.IDisplay).notification( - "Congratulations! You have successfully enabled {0}!{1}{1}" + "Congratulations! You have successfully enabled {0}{1}{1}" "You should test your configuration at:{1}{2}".format( _gen_https_names(domains), os.linesep, From 3a4d36e062c1f1f086685479fc1e0809c196a5b5 Mon Sep 17 00:00:00 2001 From: lord63 Date: Fri, 4 Dec 2015 10:21:07 +0800 Subject: [PATCH 28/57] Fix typo in README.rst and docs/using.rst --- README.rst | 2 +- docs/using.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index f25dc1956..d1f5d3428 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ If ``letsencrypt`` is packaged for your OS, you can install it from there, and run it by typing ``letsencrypt``. Because not all operating systems have packages yet, we provide a temporary solution via the ``letsencrypt-auto`` wrapper script, which obtains some dependencies from your OS and puts others -in an python virtual environment:: +in a python virtual environment:: user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt user@webserver:~$ cd letsencrypt diff --git a/docs/using.rst b/docs/using.rst index b546e3005..211eb78c8 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -286,7 +286,7 @@ get support on our `forums `_. If you find a bug in the software, please do report it in our `issue tracker `_. Remember to -give us us as much information as possible: +give us as much information as possible: - copy and paste exact command line used and the output (though mind that the latter might include some personally identifiable From 869c3741c51126a98187eb6cce2d5ea751a35416 Mon Sep 17 00:00:00 2001 From: Marius Gedminas Date: Fri, 4 Dec 2015 12:03:33 +0200 Subject: [PATCH 29/57] Typo: Apacche -> Apache --- letsencrypt-apache/letsencrypt_apache/augeas_lens/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/README b/letsencrypt-apache/letsencrypt_apache/augeas_lens/README index fc803a776..f801efd43 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_lens/README +++ b/letsencrypt-apache/letsencrypt_apache/augeas_lens/README @@ -1,2 +1,2 @@ Let's Encrypt includes the very latest Augeas lenses in order to ship bug fixes -to Apacche configuration handling bugs as quickly as possible +to Apache configuration handling bugs as quickly as possible From b4e0dfe5a8b71f004138ca8fdb7587e325569341 Mon Sep 17 00:00:00 2001 From: Seppe Stas Date: Fri, 4 Dec 2015 11:20:50 +0100 Subject: [PATCH 30/57] Fixed small typo --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index f25dc1956..d1f5d3428 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ If ``letsencrypt`` is packaged for your OS, you can install it from there, and run it by typing ``letsencrypt``. Because not all operating systems have packages yet, we provide a temporary solution via the ``letsencrypt-auto`` wrapper script, which obtains some dependencies from your OS and puts others -in an python virtual environment:: +in a python virtual environment:: user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt user@webserver:~$ cd letsencrypt From ffa4eebd900a3c5ed177933779a006d385a97151 Mon Sep 17 00:00:00 2001 From: Brandon Kraft Date: Fri, 4 Dec 2015 14:11:08 -0600 Subject: [PATCH 31/57] Correct typo in --register-unsafely-without-email --- letsencrypt/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 3652f828f..348818368 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -855,7 +855,7 @@ def prepare_and_parse_args(plugins, args): "email address. This is strongly discouraged, because in the " "event of key loss or account compromise you will irrevocably " "lose access to your account. You will also be unable to receive " - "notice about impending expiration of revocation of your " + "notice about impending expiration or revocation of your " "certificates. Updates to the Subscriber Agreement will still " "affect you, and will be effective 14 days after posting an " "update to the web site.") From df49c661247ca1f8adb235e654332dc3fbf92616 Mon Sep 17 00:00:00 2001 From: Travis Raines Date: Fri, 4 Dec 2015 22:22:32 -0800 Subject: [PATCH 32/57] Added a descriptive error if domain list includes a Unicode-encoded IDN --- letsencrypt/configuration.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index a2a54d2d0..f2221bfcb 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -144,6 +144,15 @@ def _check_config_domain_sanity(domains): if any("xn--" in d for d in domains): raise errors.ConfigurationError( "Punycode domains are not supported") + + # Unicode + try: + for domain in domains: + domain.encode('ascii',errors='strict') + except UnicodeDecodeError: + raise errors.ConfigurationError( + "Internationalized domain names are not supported") + # FQDN checks from # http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/ # Characters used, domain parts < 63 chars, tld > 1 < 64 chars From 2f71b2c0bee4c2abd76f76cacd1a3cf2ac56c1e9 Mon Sep 17 00:00:00 2001 From: Travis Raines Date: Fri, 4 Dec 2015 22:44:17 -0800 Subject: [PATCH 33/57] fixing whitespace lint and version incompatibility at once! --- letsencrypt/configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index f2221bfcb..69778f5f0 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -148,7 +148,7 @@ def _check_config_domain_sanity(domains): # Unicode try: for domain in domains: - domain.encode('ascii',errors='strict') + domain.encode('ascii') except UnicodeDecodeError: raise errors.ConfigurationError( "Internationalized domain names are not supported") From 753022d8e36f2794696a9fde17d3f535961fa3eb Mon Sep 17 00:00:00 2001 From: Gene Wood Date: Sat, 5 Dec 2015 11:02:14 -0800 Subject: [PATCH 34/57] Clarify error messages with acronym DV --- acme/acme/messages.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/acme/acme/messages.py b/acme/acme/messages.py index 0b9ea8105..0b73864ec 100644 --- a/acme/acme/messages.py +++ b/acme/acme/messages.py @@ -22,12 +22,14 @@ class Error(jose.JSONObjectWithFields, errors.Error): ('urn:acme:error:' + name, description) for name, description in ( ('badCSR', 'The CSR is unacceptable (e.g., due to a short key)'), ('badNonce', 'The client sent an unacceptable anti-replay nonce'), - ('connection', 'The server could not connect to the client for DV'), + ('connection', 'The server could not connect to the client to ' + 'verify the domain'), ('dnssec', 'The server could not validate a DNSSEC signed domain'), ('malformed', 'The request message was malformed'), ('rateLimited', 'There were too many requests of a given type'), ('serverInternal', 'The server experienced an internal error'), - ('tls', 'The server experienced a TLS error during DV'), + ('tls', 'The server experienced a TLS error during domain ' + 'verification'), ('unauthorized', 'The client lacks sufficient authorization'), ('unknownHost', 'The server could not resolve a domain name'), ) From 55097af38abe271521e791559bb24f3adbd56a80 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Sat, 5 Dec 2015 11:03:58 -0800 Subject: [PATCH 35/57] Document passing domains via config file. closes #1771 --- examples/cli.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/cli.ini b/examples/cli.ini index a20764ed8..c8678f89c 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -11,6 +11,10 @@ server = https://acme-staging.api.letsencrypt.org/directory # Uncomment and update to register with the specified e-mail address # email = foo@example.com +# Uncommon and update to generate certificates for the specified +# domains. +# domains = example.com, www.example.com + # Uncomment to use a text interface instead of ncurses # text = True From cb6ecea087e9a83a8bf5e4452c498f8cdb57f9e1 Mon Sep 17 00:00:00 2001 From: Nelson Elhage Date: Sat, 5 Dec 2015 11:35:54 -0800 Subject: [PATCH 36/57] Fix a typo in example config file. --- examples/cli.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cli.ini b/examples/cli.ini index c8678f89c..6b6b05d7d 100644 --- a/examples/cli.ini +++ b/examples/cli.ini @@ -11,7 +11,7 @@ server = https://acme-staging.api.letsencrypt.org/directory # Uncomment and update to register with the specified e-mail address # email = foo@example.com -# Uncommon and update to generate certificates for the specified +# Uncomment and update to generate certificates for the specified # domains. # domains = example.com, www.example.com From f2a93e00ea023768592be25c62691cef74be8181 Mon Sep 17 00:00:00 2001 From: Devin Howard Date: Sun, 6 Dec 2015 18:20:11 +0800 Subject: [PATCH 37/57] Mention the --renew-by-default flag I was going crazy looking for this flag - I think it's worth a mention in the Renewal section --- docs/using.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index b546e3005..6e15d2cf2 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -173,10 +173,11 @@ Renewal In order to renew certificates simply call the ``letsencrypt`` (or letsencrypt-auto_) again, and use the same values when prompted. You can automate it slightly by passing necessary flags on the CLI (see -`--help all`), or even further using the :ref:`config-file`. If you're -sure that UI doesn't prompt for any details you can add the command to -``crontab`` (make it less than every 90 days to avoid problems, say -every month). +`--help all`), or even further using the :ref:`config-file`. The +``--renew-by-default`` flag may be helpful for automating renewal. If +you're sure that UI doesn't prompt for any details you can add the +command to ``crontab`` (make it less than every 90 days to avoid +problems, say every month). 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. From f1a50b08fb86974e0f907453a498ab7526fce906 Mon Sep 17 00:00:00 2001 From: Sveder Date: Mon, 7 Dec 2015 02:02:47 +0200 Subject: [PATCH 38/57] Changed freenode to link straight to the web IRC page for the letsencrypt channel. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d1f5d3428..57908e90f 100644 --- a/README.rst +++ b/README.rst @@ -163,5 +163,5 @@ Current Features * Free and Open Source Software, made with Python. -.. _Freenode: https://freenode.net +.. _Freenode: https://webchat.freenode.net?channels=%23letsencrypt .. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev From 312669c64d1fc05716cd892d3579b0cf6b51d15b Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Mon, 7 Dec 2015 10:20:03 +0000 Subject: [PATCH 39/57] Merge Augeas lens fix for closing multiple sections on one line From https://github.com/hercules-team/augeas/commit/f44a7a55cc7162beced99659234eb078a8d20e1d Closes: #1693 --- letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug | 2 +- .../{failing => passing}/two-blocks-one-line-1693.conf | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/apache-conf-files/{failing => passing}/two-blocks-one-line-1693.conf (100%) diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug index 30d8ca501..dc30464a8 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug +++ b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug @@ -91,7 +91,7 @@ let section (body:lens) = indent . dels "" ">" . eol ] + [ indent . dels "<" . square kword inner dword . del />[ \t\n\r]*/ ">\n" ] let rec content = section (content|directive) diff --git a/tests/apache-conf-files/failing/two-blocks-one-line-1693.conf b/tests/apache-conf-files/passing/two-blocks-one-line-1693.conf similarity index 100% rename from tests/apache-conf-files/failing/two-blocks-one-line-1693.conf rename to tests/apache-conf-files/passing/two-blocks-one-line-1693.conf From 2d5d4a65c45ea379f847bcd9effd9c05a4b50556 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 15:07:27 +0200 Subject: [PATCH 40/57] Moved domain check to le_util --- letsencrypt/configuration.py | 40 +++--------------------------------- letsencrypt/le_util.py | 34 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index 69778f5f0..6de529981 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -8,6 +8,7 @@ import zope.interface from letsencrypt import constants from letsencrypt import errors from letsencrypt import interfaces +from letsencrypt import le_util class NamespaceConfig(object): @@ -123,40 +124,5 @@ def check_config_sanity(config): # Domain checks if config.namespace.domains is not None: - _check_config_domain_sanity(config.namespace.domains) - - -def _check_config_domain_sanity(domains): - """Helper method for check_config_sanity which validates - domain flag values and errors out if the requirements are not met. - - :param domains: List of domains - :type domains: `list` of `string` - :raises ConfigurationError: for invalid domains and cases where Let's - Encrypt currently will not issue certificates - - """ - # Check if there's a wildcard domain - if any(d.startswith("*.") for d in domains): - raise errors.ConfigurationError( - "Wildcard domains are not supported") - # Punycode - if any("xn--" in d for d in domains): - raise errors.ConfigurationError( - "Punycode domains are not supported") - - # Unicode - try: - for domain in domains: - domain.encode('ascii') - except UnicodeDecodeError: - raise errors.ConfigurationError( - "Internationalized domain names are not supported") - - # FQDN checks from - # http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/ - # Characters used, domain parts < 63 chars, tld > 1 < 64 chars - # first and last char is not "-" - fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(? 1 < 64 chars + # first and last char is not "-" + fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(? Date: Mon, 7 Dec 2015 15:37:09 +0200 Subject: [PATCH 41/57] Added domain checks for apache installer --- letsencrypt-apache/letsencrypt_apache/configurator.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 98b0b8820..50e5ed6be 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -369,7 +369,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): vhost_macro = [] for vhost in self.vhosts: - all_names.update(vhost.get_names()) + # Check domains for validity + for name in vhost.get_names(): + try: + le_util.check_domain_sanity(name) + all_names.add(name) + except errors.ConfigurationError: + pass if vhost.modmacro: vhost_macro.append(vhost.filep) From 82f71cba9ba0df6f639c996a64dcd4e5122a39a6 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Mon, 7 Dec 2015 16:02:27 +0200 Subject: [PATCH 42/57] Linter fixes --- letsencrypt/configuration.py | 1 - letsencrypt/le_util.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/letsencrypt/configuration.py b/letsencrypt/configuration.py index 6de529981..afd5edbe4 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -1,7 +1,6 @@ """Let's Encrypt user-supplied configuration.""" import os import urlparse -import re import zope.interface diff --git a/letsencrypt/le_util.py b/letsencrypt/le_util.py index 97f983ea2..e5e252871 100644 --- a/letsencrypt/le_util.py +++ b/letsencrypt/le_util.py @@ -313,4 +313,4 @@ def check_domain_sanity(domain): # first and last char is not "-" fqdn = re.compile("^((?!-)[A-Za-z0-9-]{1,63}(? Date: Mon, 7 Dec 2015 16:05:53 +0200 Subject: [PATCH 43/57] Corrected tests to reflect the removal of wildcard domains etc. --- .../letsencrypt_apache/tests/configurator_test.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index fcccfaae2..986b060f5 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -64,7 +64,7 @@ class TwoVhost80Test(util.ApacheTest): mock_getutility.notification = mock.MagicMock(return_value=True) names = self.config.get_all_names() self.assertEqual(names, set( - ["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17"])) + ["letsencrypt.demo", "encryption-example.demo"])) @mock.patch("zope.component.getUtility") @mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr") @@ -82,7 +82,7 @@ class TwoVhost80Test(util.ApacheTest): self.config.vhosts.append(vhost) names = self.config.get_all_names() - self.assertEqual(len(names), 5) + self.assertEqual(len(names), 4) self.assertTrue("zombo.com" in names) self.assertTrue("google.com" in names) self.assertTrue("letsencrypt.demo" in names) @@ -90,10 +90,17 @@ class TwoVhost80Test(util.ApacheTest): def test_add_servernames_alias(self): self.config.parser.add_dir( self.vh_truth[2].path, "ServerAlias", ["*.le.co"]) + self.config.parser.add_dir( + self.vh_truth[0].path, "ServerAlias", ["working.example.com"]) + self.config._add_servernames(self.vh_truth[2]) # pylint: disable=protected-access + self.config._add_servernames(self.vh_truth[0]) # pylint: disable=protected-access self.assertEqual( self.vh_truth[2].get_names(), set(["*.le.co", "ip-172-30-0-17"])) + self.assertEqual( + self.vh_truth[0].get_names(), set(["working.example.com", + "encryption-example.demo"])) def test_get_virtual_hosts(self): """Make sure all vhosts are being properly found. From d81620ccdd0e1eb3a07f3a2013bb8108e336d56b Mon Sep 17 00:00:00 2001 From: Viktor Szakats Date: Tue, 8 Dec 2015 02:49:59 +0100 Subject: [PATCH 44/57] _rpm_common.sh: minor typo --- bootstrap/_rpm_common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index b975da444..411d7bd92 100755 --- a/bootstrap/_rpm_common.sh +++ b/bootstrap/_rpm_common.sh @@ -2,7 +2,7 @@ # Tested with: # - Fedora 22, 23 (x64) -# - Centos 7 (x64: onD igitalOcean droplet) +# - Centos 7 (x64: on DigitalOcean droplet) if type dnf 2>/dev/null then From 51a5d7ceb085ac03593adafc63bfb6357d96eb34 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 09:05:11 +0200 Subject: [PATCH 45/57] Move validation code to main client --- letsencrypt/display/ops.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index 038ad6fdc..ca9c8c126 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -186,7 +186,8 @@ def choose_names(installer): logger.debug("No installer, picking names manually") return _choose_names_manually() - names = list(installer.get_all_names()) + domains = list(installer.get_all_names()) + names = get_valid_domains(domains) if not names: manual = util(interfaces.IDisplay).yesno( @@ -207,6 +208,22 @@ def choose_names(installer): else: return [] +def get_valid_domains(self, domains): + """Helper method for choose_names that implements basic checks + on domain names + + :param list domains: Domain names to validate + :return: List of valid domains + :rtype: list + """ + valid_domains = [] + for domain in domains: + try: + le_util.check_domain_sanity(domain) + valid_domains.append(domain) + except errors.ConfigurationError: + continue + return valid_domains def _filter_names(names): """Determine which names the user would like to select from a list. From d8b83bc478ac3ff875a604ce315c16c593e36170 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 09:05:33 +0200 Subject: [PATCH 46/57] Revert "Corrected tests to reflect the removal of wildcard domains etc." This reverts commit 53a4d0725dbf0c5f728094dc318f491c9478effd. --- .../letsencrypt_apache/tests/configurator_test.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index 986b060f5..fcccfaae2 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -64,7 +64,7 @@ class TwoVhost80Test(util.ApacheTest): mock_getutility.notification = mock.MagicMock(return_value=True) names = self.config.get_all_names() self.assertEqual(names, set( - ["letsencrypt.demo", "encryption-example.demo"])) + ["letsencrypt.demo", "encryption-example.demo", "ip-172-30-0-17"])) @mock.patch("zope.component.getUtility") @mock.patch("letsencrypt_apache.configurator.socket.gethostbyaddr") @@ -82,7 +82,7 @@ class TwoVhost80Test(util.ApacheTest): self.config.vhosts.append(vhost) names = self.config.get_all_names() - self.assertEqual(len(names), 4) + self.assertEqual(len(names), 5) self.assertTrue("zombo.com" in names) self.assertTrue("google.com" in names) self.assertTrue("letsencrypt.demo" in names) @@ -90,17 +90,10 @@ class TwoVhost80Test(util.ApacheTest): def test_add_servernames_alias(self): self.config.parser.add_dir( self.vh_truth[2].path, "ServerAlias", ["*.le.co"]) - self.config.parser.add_dir( - self.vh_truth[0].path, "ServerAlias", ["working.example.com"]) - self.config._add_servernames(self.vh_truth[2]) # pylint: disable=protected-access - self.config._add_servernames(self.vh_truth[0]) # pylint: disable=protected-access self.assertEqual( self.vh_truth[2].get_names(), set(["*.le.co", "ip-172-30-0-17"])) - self.assertEqual( - self.vh_truth[0].get_names(), set(["working.example.com", - "encryption-example.demo"])) def test_get_virtual_hosts(self): """Make sure all vhosts are being properly found. From e891624cb1159c498167fa76c8225c3176fcf4b4 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 09:07:17 +0200 Subject: [PATCH 47/57] Revert "Added domain checks for apache installer" This reverts commit 5dcd5088273dcf4dcb402bcd8a69a655d29fa383. --- letsencrypt-apache/letsencrypt_apache/configurator.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 50e5ed6be..98b0b8820 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -369,13 +369,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): vhost_macro = [] for vhost in self.vhosts: - # Check domains for validity - for name in vhost.get_names(): - try: - le_util.check_domain_sanity(name) - all_names.add(name) - except errors.ConfigurationError: - pass + all_names.update(vhost.get_names()) if vhost.modmacro: vhost_macro.append(vhost.filep) From 3c1c3c3e8dbf6b0ae7e50032c76a25a4fba7f992 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 09:31:47 +0200 Subject: [PATCH 48/57] Import and non-classmethod fix --- letsencrypt/display/ops.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/letsencrypt/display/ops.py b/letsencrypt/display/ops.py index ca9c8c126..941a0a114 100644 --- a/letsencrypt/display/ops.py +++ b/letsencrypt/display/ops.py @@ -4,6 +4,7 @@ import os import zope.component +from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt.display import util as display_util @@ -208,7 +209,7 @@ def choose_names(installer): else: return [] -def get_valid_domains(self, domains): +def get_valid_domains(domains): """Helper method for choose_names that implements basic checks on domain names From 0fb4f7dc8bde20afc6746b14438aa65c356eff07 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 09:32:02 +0200 Subject: [PATCH 49/57] Tests for domain validation --- letsencrypt/tests/display/ops_test.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index b0b905c33..60874a007 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -385,7 +385,17 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(self._call(self.mock_install), []) + def test_get_valid_domains(self): + from letsencrypt.display_ops import get_valid_domains + all_valid = ["example.com", "second.example.com", + "also.example.com"] + all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN"] + two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"] + self.assertEqual(get_valid_domains(all_valid), all_valid) + self.assertEqual(get_valid_domains(all_invalid), []) + self.assertEqual(len(get_valid_domains(two_valid)), 2) + class SuccessInstallationTest(unittest.TestCase): # pylint: disable=too-few-public-methods """Test the success installation message.""" From 5c8b493eda2e5fa707ed6299d2ee932e7921a467 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 09:54:56 +0200 Subject: [PATCH 50/57] Whitespace and dot fix --- letsencrypt/tests/display/ops_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index 60874a007..5958b37a1 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -386,7 +386,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(self._call(self.mock_install), []) def test_get_valid_domains(self): - from letsencrypt.display_ops import get_valid_domains + from letsencrypt.display.ops import get_valid_domains all_valid = ["example.com", "second.example.com", "also.example.com"] all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN"] @@ -395,7 +395,7 @@ class ChooseNamesTest(unittest.TestCase): self.assertEqual(get_valid_domains(all_invalid), []) self.assertEqual(len(get_valid_domains(two_valid)), 2) - + class SuccessInstallationTest(unittest.TestCase): # pylint: disable=too-few-public-methods """Test the success installation message.""" From f479497d6c7ab44d3b34dc800bf583e46634dcf7 Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Mon, 7 Dec 2015 10:53:15 +0000 Subject: [PATCH 51/57] Add failing test from ticket #1766 Augeas fails to parse the wordlist (args inside braces) in the SSLRequire directive. --- tests/apache-conf-files/failing/sslrequire-wordlist.conf | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/apache-conf-files/failing/sslrequire-wordlist.conf diff --git a/tests/apache-conf-files/failing/sslrequire-wordlist.conf b/tests/apache-conf-files/failing/sslrequire-wordlist.conf new file mode 100644 index 000000000..1c06d5497 --- /dev/null +++ b/tests/apache-conf-files/failing/sslrequire-wordlist.conf @@ -0,0 +1 @@ +SSLRequire %{SSL_CLIENT_S_DN_CN} in {"foo@bar.com", "bar@foo.com"} From d7616461676d145ed361588499f1b0c96739ed4a Mon Sep 17 00:00:00 2001 From: Dominic Cleal Date: Tue, 8 Dec 2015 08:04:10 +0000 Subject: [PATCH 52/57] Merge Augeas lens fix for SSLRequire wordlists From https://github.com/hercules-team/augeas/commit/f86a28d03a5c42a6c58293667a95d7794e30a42f Closes: #1766 --- .../letsencrypt_apache/augeas_lens/httpd.aug | 14 ++++++++++++-- .../{failing => passing}/sslrequire-wordlist.conf | 0 2 files changed, 12 insertions(+), 2 deletions(-) rename tests/apache-conf-files/{failing => passing}/sslrequire-wordlist.conf (100%) diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug index 83d97f7a4..f54f9fbaa 100644 --- a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug +++ b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug @@ -59,8 +59,10 @@ let empty = Util.empty_dos let indent = Util.indent (* borrowed from shellvars.aug *) -let char_arg_dir = /([^\\ '"\t\r\n]|[^ '"\t\r\n]+[^\\ '"\t\r\n])|\\\\"|\\\\'/ +let char_arg_dir = /([^\\ '"{\t\r\n]|[^ '"{\t\r\n]+[^\\ '"\t\r\n])|\\\\"|\\\\'/ let char_arg_sec = /[^ '"\t\r\n>]|\\\\"|\\\\'/ +let char_arg_wl = /([^\\ '"},\t\r\n]|[^ '"},\t\r\n]+[^\\ '"},\t\r\n])/ + let cdot = /\\\\./ let cl = /\\\\\n/ let dquot = @@ -77,11 +79,19 @@ let comp = /[<>=]?=/ let arg_dir = [ label "arg" . store (char_arg_dir+|dquot|squot) ] let arg_sec = [ label "arg" . store (char_arg_sec+|comp|dquot|squot) ] +let arg_wl = [ label "arg" . store (char_arg_wl+|dquot|squot) ] + +(* comma-separated wordlist as permitted in the SSLRequire directive *) +let arg_wordlist = + let wl_start = Util.del_str "{" in + let wl_end = Util.del_str "}" in + let wl_sep = del /[ \t]*,[ \t]*/ ", " + in [ label "wordlist" . wl_start . arg_wl . (wl_sep . arg_wl)* . wl_end ] let argv (l:lens) = l . (sep_spc . l)* let directive = [ indent . label "directive" . store word . - (sep_spc . argv arg_dir)? . eol ] + (sep_spc . argv (arg_dir|arg_wordlist))? . eol ] let section (body:lens) = (* opt_eol includes empty lines *) diff --git a/tests/apache-conf-files/failing/sslrequire-wordlist.conf b/tests/apache-conf-files/passing/sslrequire-wordlist.conf similarity index 100% rename from tests/apache-conf-files/failing/sslrequire-wordlist.conf rename to tests/apache-conf-files/passing/sslrequire-wordlist.conf From 0cb80bf7534660d3df30bc12d4b8350e4468dd24 Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 14:23:46 +0200 Subject: [PATCH 53/57] Better test coverage --- letsencrypt/tests/display/ops_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/letsencrypt/tests/display/ops_test.py b/letsencrypt/tests/display/ops_test.py index 5958b37a1..30183b955 100644 --- a/letsencrypt/tests/display/ops_test.py +++ b/letsencrypt/tests/display/ops_test.py @@ -1,3 +1,4 @@ +# coding=utf-8 """Test letsencrypt.display.ops.""" import os import sys @@ -389,7 +390,8 @@ class ChooseNamesTest(unittest.TestCase): from letsencrypt.display.ops import get_valid_domains all_valid = ["example.com", "second.example.com", "also.example.com"] - all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN"] + all_invalid = ["xn--ls8h.tld", "*.wildcard.com", "notFQDN", + "uniçodé.com"] two_valid = ["example.com", "xn--ls8h.tld", "also.example.com"] self.assertEqual(get_valid_domains(all_valid), all_valid) self.assertEqual(get_valid_domains(all_invalid), []) From 62ea74b9e4a66afedcf4625667e082497b719eea Mon Sep 17 00:00:00 2001 From: Ingolf Becker Date: Tue, 8 Dec 2015 13:22:52 +0000 Subject: [PATCH 54/57] Modify apache plugin to work on setups where apache listens to a specific ip --- .../letsencrypt_apache/configurator.py | 50 +++++++++++++------ .../tests/configurator_test.py | 33 ++++++++++++ 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 98b0b8820..76045bee1 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -545,21 +545,43 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Check for Listen # Note: This could be made to also look for ip:443 combo - if not self.parser.find_dir("Listen", port): - logger.debug("No Listen %s directive found. Setting the " - "Apache Server to Listen on port %s", port, port) - - if port == "443": - args = [port] + listens = [self.parser.get_arg(x).split()[0] for x in self.parser.find_dir("Listen")] + # In case no Listens are set (which really is a broken apache config) + if not listens: + listens = ["80"] + for listen in listens: + # For any listen statement, check if the machine also listens on Port 443. + # If not, add such a listen statement. + if len(listen.split(":")) == 1: + # Its listening to all interfaces + if port not in listens: + if port == "443": + args = [port] + else: + # Non-standard ports should specify https protocol + args = [port, "https"] + self.parser.add_dir_to_ifmodssl( + parser.get_aug_path( + self.parser.loc["listen"]), "Listen", args) + self.save_notes += "Added Listen %s directive to %s\n" % ( + port, self.parser.loc["listen"]) + listens.append(port) else: - # Non-standard ports should specify https protocol - args = [port, "https"] - - self.parser.add_dir_to_ifmodssl( - parser.get_aug_path( - self.parser.loc["listen"]), "Listen", args) - self.save_notes += "Added Listen %s directive to %s\n" % ( - port, self.parser.loc["listen"]) + # The Listen statement specifies an ip + _, ip = listen[::-1].split(":", 1) + ip = ip[::-1] + if "%s:%s" %(ip, port) not in listens: + if port == "443": + args = ["%s:%s" %(ip, port)] + else: + # Non-standard ports should specify https protocol + args = ["%s:%s" %(ip, port), "https"] + self.parser.add_dir_to_ifmodssl( + parser.get_aug_path( + self.parser.loc["listen"]), "Listen", args) + self.save_notes += "Added Listen %s:%s directive to %s\n" % ( + ip, port, self.parser.loc["listen"]) + listens.append("%s:%s" %(ip, port)) def make_addrs_sni_ready(self, addrs): """Checks to see if the server is ready for SNI challenges. diff --git a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py index fcccfaae2..991704144 100644 --- a/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py +++ b/letsencrypt-apache/letsencrypt_apache/tests/configurator_test.py @@ -391,6 +391,39 @@ class TwoVhost80Test(util.ApacheTest): self.assertEqual(mock_add_dir.call_count, 2) + def test_prepare_server_https_named_listen(self): + mock_find = mock.Mock() + mock_find.return_value = ["test1", "test2", "test3"] + mock_get = mock.Mock() + mock_get.side_effect = ["1.2.3.4:80", "[::1]:80", "1.1.1.1:443"] + mock_add_dir = mock.Mock() + mock_enable = mock.Mock() + + self.config.parser.find_dir = mock_find + self.config.parser.get_arg = mock_get + self.config.parser.add_dir_to_ifmodssl = mock_add_dir + self.config.enable_mod = mock_enable + + # Test Listen statements with specific ip listeed + self.config.prepare_server_https("443") + # Should only be 2 here, as the third interface already listens to the correct port + self.assertEqual(mock_add_dir.call_count, 2) + + # Check argument to new Listen statements + self.assertEqual(mock_add_dir.call_args_list[0][0][2], ["1.2.3.4:443"]) + self.assertEqual(mock_add_dir.call_args_list[1][0][2], ["[::1]:443"]) + + # Reset return lists and inputs + mock_add_dir.reset_mock() + mock_get.side_effect = ["1.2.3.4:80", "[::1]:80", "1.1.1.1:443"] + + # Test + self.config.prepare_server_https("8080", temp=True) + self.assertEqual(mock_add_dir.call_count, 3) + self.assertEqual(mock_add_dir.call_args_list[0][0][2], ["1.2.3.4:8080", "https"]) + self.assertEqual(mock_add_dir.call_args_list[1][0][2], ["[::1]:8080", "https"]) + self.assertEqual(mock_add_dir.call_args_list[2][0][2], ["1.1.1.1:8080", "https"]) + def test_make_vhost_ssl(self): ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0]) From bbd6534744c7c8d91a1be0836c6e069a16eab71b Mon Sep 17 00:00:00 2001 From: Ingolf Becker Date: Tue, 8 Dec 2015 17:56:16 +0000 Subject: [PATCH 55/57] Fixed some pep8 formatting --- .../letsencrypt_apache/configurator.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 76045bee1..1d39e7fdf 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -120,7 +120,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.version = version self.vhosts = None self._enhance_func = {"redirect": self._enable_redirect, - "ensure-http-header": self._set_http_header} + "ensure-http-header": self._set_http_header} @property def mod_ssl_conf(self): @@ -570,18 +570,18 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # The Listen statement specifies an ip _, ip = listen[::-1].split(":", 1) ip = ip[::-1] - if "%s:%s" %(ip, port) not in listens: + if "%s:%s" % (ip, port) not in listens: if port == "443": - args = ["%s:%s" %(ip, port)] + args = ["%s:%s" % (ip, port)] else: # Non-standard ports should specify https protocol - args = ["%s:%s" %(ip, port), "https"] + args = ["%s:%s" % (ip, port), "https"] self.parser.add_dir_to_ifmodssl( parser.get_aug_path( self.parser.loc["listen"]), "Listen", args) self.save_notes += "Added Listen %s:%s directive to %s\n" % ( - ip, port, self.parser.loc["listen"]) - listens.append("%s:%s" %(ip, port)) + ip, port, self.parser.loc["listen"]) + listens.append("%s:%s" % (ip, port)) def make_addrs_sni_ready(self, addrs): """Checks to see if the server is ready for SNI challenges. From 006edfdbd1953183d09d8624aeb13bba8e07babe Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 21:08:30 +0200 Subject: [PATCH 56/57] Wording change to error messages --- letsencrypt/le_util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/letsencrypt/le_util.py b/letsencrypt/le_util.py index e5e252871..7c7f0b7f7 100644 --- a/letsencrypt/le_util.py +++ b/letsencrypt/le_util.py @@ -294,18 +294,18 @@ def check_domain_sanity(domain): # Check if there's a wildcard domain if domain.startswith("*."): raise errors.ConfigurationError( - "Wildcard domains are not supported") + "Wildcard domains are not presently supported") # Punycode if "xn--" in domain: raise errors.ConfigurationError( - "Punycode domains are not supported") + "Punycode domains are not presently supported") # Unicode try: domain.encode('ascii') except UnicodeDecodeError: raise errors.ConfigurationError( - "Internationalized domain names are not supported") + "Internationalized domain names are not presently supported") # FQDN checks from # http://www.mkyong.com/regular-expressions/domain-name-regular-expression-example/ From 05723cbb4afb275ffd27e090b7801ea6d42ef5cf Mon Sep 17 00:00:00 2001 From: Joona Hoikkala Date: Tue, 8 Dec 2015 21:28:23 +0200 Subject: [PATCH 57/57] Fixed wording --- letsencrypt/le_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/letsencrypt/le_util.py b/letsencrypt/le_util.py index 7c7f0b7f7..fe63c70af 100644 --- a/letsencrypt/le_util.py +++ b/letsencrypt/le_util.py @@ -294,7 +294,7 @@ def check_domain_sanity(domain): # Check if there's a wildcard domain if domain.startswith("*."): raise errors.ConfigurationError( - "Wildcard domains are not presently supported") + "Wildcard domains are not supported") # Punycode if "xn--" in domain: raise errors.ConfigurationError(