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/README.rst b/README.rst index f38b09cfd..57908e90f 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 @@ -118,6 +118,24 @@ email to client-dev+subscribe@letsencrypt.org) .. _watch demo video: https://www.youtube.com/watch?v=Gas_sSB-5SU +System Requirements +=================== + +The Let's Encrypt Client presently only runs on Unix-ish OSes that include +Python 2.6 or 2.7; Python 3.x support will be added after the Public Beta +launch. The client requires root access in order to write to +``/etc/letsencrypt``, ``/var/log/letsencrypt``, ``/var/lib/letsencrypt``; to +bind to ports 80 and 443 (if you use the ``standalone`` plugin) and to read and +modify webserver configurations (if you use the ``apache`` or ``nginx`` +plugins). If none of these apply to you, it is theoretically possible to run +without root privileges, but for most users who want to avoid running an ACME +client as root, either `letsencrypt-nosudo +`_ or `simp_le +`_ are more appropriate choices. + +The Apache plugin currently requires a Debian-based OS with augeas version +1.0; this includes Ubuntu 12.04+ and Debian 7+. + Current Features ================ @@ -145,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 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 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'), ) diff --git a/acme/setup.py b/acme/setup.py index a6551a023..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.dev0' +version = '0.2.0.dev0' install_requires = [ # load_pem_private/public_key (>=0.6) diff --git a/bootstrap/_rpm_common.sh b/bootstrap/_rpm_common.sh index 57b28240e..605cf6ca3 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 diff --git a/docs/using.rst b/docs/using.rst index d6ae2c5ee..51426183d 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,107 +56,48 @@ 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, follow the instructions in +the :doc:`contributing`. Some `other methods of installation`_ are discussed +below. Plugins ======= -=========== = = =============================================================== -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_. -=========== = = =============================================================== +The Let's Encrypt client supports a number of different "plugins" that can be +used to obtain and/or install certificates. Plugins that can obtain a cert +are called "authenticators" and can be used with the "certonly" command. +Plugins that can install a cert are called "installers". Plugins that do both +can be used with the "letsencrypt run" command, which is the default. + +=========== ==== ==== =============================================================== +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 ------ 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``. @@ -184,13 +120,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 ------ @@ -228,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. @@ -341,7 +287,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 @@ -352,6 +298,107 @@ 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 + +**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** + +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 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 +recommended or supported. + .. rubric:: Footnotes diff --git a/examples/cli.ini b/examples/cli.ini index a20764ed8..6b6b05d7d 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 +# Uncomment 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 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-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 diff --git a/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug b/letsencrypt-apache/letsencrypt_apache/augeas_lens/httpd.aug index 30d8ca501..0669896a0 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]*[^\\ '"\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 *) @@ -91,7 +101,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/letsencrypt-apache/letsencrypt_apache/configurator.py b/letsencrypt-apache/letsencrypt_apache/configurator.py index 52635ca11..3ad6032f1 100644 --- a/letsencrypt-apache/letsencrypt_apache/configurator.py +++ b/letsencrypt-apache/letsencrypt_apache/configurator.py @@ -131,7 +131,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): @@ -558,21 +558,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 50b23b815..b93034cd9 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]) diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index e4dd11935..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.dev0' +version = '0.2.0.dev0' install_requires = [ 'acme=={0}'.format(version), 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 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 a669ad841..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.dev0' +version = '0.2.0.dev0' install_requires = [ 'acme=={0}'.format(version), 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/__init__.py b/letsencrypt/__init__.py index 1155a5b0c..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.0.dev0' +__version__ = '0.2.0.dev0' diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 0498d66c4..348818368 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -9,7 +9,6 @@ import json import logging import logging.handlers import os -import pkg_resources import sys import time import traceback @@ -304,6 +303,14 @@ 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(): + "Suggest a donation to support Let's Encrypt" + reporter_util = zope.component.getUtility(interfaces.IReporter) + msg = ("If you like Let's Encrypt, please consider supporting our work by:\n\n" + "Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate\n" + "Donating to EFF: https://eff.org/donate-le\n\n") + reporter_util.add_message(msg, reporter_util.LOW_PRIORITY) + def _auth_from_domains(le_client, config, domains): """Authenticate and enroll certificate.""" @@ -473,6 +480,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 +511,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.""" @@ -750,6 +761,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): """ @@ -830,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.") @@ -858,10 +883,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") - helpful.add( - "automation", "--agree-dev-preview", action="store_true", - help="Agree to the Let's Encrypt Developer Preview Disclaimer") + "previously attained cert") helpful.add( "automation", "--agree-tos", dest="tos", action="store_true", help="Agree to the Let's Encrypt Subscriber Agreement") @@ -926,6 +948,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 @@ -1055,7 +1079,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 @@ -1245,13 +1269,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/configuration.py b/letsencrypt/configuration.py index a2a54d2d0..afd5edbe4 100644 --- a/letsencrypt/configuration.py +++ b/letsencrypt/configuration.py @@ -1,13 +1,13 @@ """Let's Encrypt user-supplied configuration.""" import os import urlparse -import re import zope.interface from letsencrypt import constants from letsencrypt import errors from letsencrypt import interfaces +from letsencrypt import le_util class NamespaceConfig(object): @@ -123,31 +123,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") - # 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}(? + + 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 + 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 \ 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