diff --git a/CHANGES.rst b/CHANGES.rst index 55e4bd796..4ce41a8bc 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,9 +3,9 @@ ChangeLog Please note: the change log will only get updated after first release - for now please use the -`commit log `_. +`commit log `_. To see the changes in a given release, inspect the github milestone for the release. For instance: -https://github.com/letsencrypt/letsencrypt/issues?utf8=%E2%9C%93&q=milestone%3A0.3.0 +https://github.com/certbot/certbot/issues?utf8=%E2%9C%93&q=milestone%3A0.3.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bf19b18e1..5f1625658 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,4 +15,4 @@ to the Sphinx generated docs is provided below. --> -https://letsencrypt.readthedocs.org/en/latest/contributing.html +https://certbot.eff.org/docs/contributing.html diff --git a/Dockerfile b/Dockerfile index 3e4c9430e..d42b632d4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,7 @@ WORKDIR /opt/certbot # If doesn't exist, it is created along with all missing # directories in its path. +ENV DEBIAN_FRONTEND=noninteractive COPY letsencrypt-auto-source/letsencrypt-auto /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto RUN /opt/certbot/src/letsencrypt-auto-source/letsencrypt-auto --os-packages-only && \ diff --git a/README.rst b/README.rst index 91a3cfcb5..c71079f9a 100644 --- a/README.rst +++ b/README.rst @@ -11,43 +11,63 @@ For more information regarding the status of the project, please see https://letsencrypt.org. Be sure to checkout the `Frequently Asked Questions (FAQ) `_. -About the Let's Encrypt Client +About Certbot ============================== -The Let's Encrypt Client is a fully-featured, extensible client for the Let's +Certbot is a fully-featured, extensible client for the Let's Encrypt CA (or any other CA that speaks the `ACME `_ protocol) that can automate the tasks of obtaining certificates and configuring webservers to use them. This client runs on Unix-based operating systems. +Until May 2016, Certbot was named simply ``letsencrypt`` or ``letsencrypt-auto``, +depending on install method. Instructions on the Internet, and some pieces of the +software, may still refer to this older name. + +Contributing +------------ + +If you'd like to contribute to this project please read `Developer Guide +`_. + +.. _installation: + Installation ------------ -If ``letsencrypt`` is packaged for your Unix 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 a python virtual environment:: +If ``certbot`` (or ``letsencrypt``) is packaged for your Unix OS (visit +certbot.eff.org_ to find out), you can install it +from there, and run it by typing ``certbot`` (or ``letsencrypt``). Because +not all operating systems have packages yet, we provide a temporary solution +via the ``certbot-auto`` wrapper script, which obtains some dependencies from +your OS and puts others in a python virtual environment:: - user@webserver:~$ git clone https://github.com/letsencrypt/letsencrypt - user@webserver:~$ cd letsencrypt - user@webserver:~/letsencrypt$ ./letsencrypt-auto --help + user@webserver:~$ wget https://dl.eff.org/certbot-auto + user@webserver:~$ chmod a+x ./certbot-auto + user@webserver:~$ ./certbot-auto --help -Or for full command line help, type:: +.. hint:: The certbot-auto download is protected by HTTPS, which is pretty good, but if you'd like to + double check the integrity of the ``certbot-auto`` script, you can use these steps for verification before running it:: - ./letsencrypt-auto --help all + user@server:~$ wget -N https://dl.eff.org/certbot-auto.asc + user@server:~$ gpg2 --recv-key A2CFB51FA275A7286234E7B24D17C995CD9775F2 + user@server:~$ gpg2 --trusted-key 4D17C995CD9775F2 --verify certbot-auto.asc certbot-auto -``letsencrypt-auto`` updates to the latest client release automatically. And -since ``letsencrypt-auto`` is a wrapper to ``letsencrypt``, it accepts exactly +And for full command line help, you can type:: + + ./certbot-auto --help all + +``certbot-auto`` updates to the latest client release automatically. And +since ``certbot-auto`` is a wrapper to ``certbot``, it accepts exactly the same command line flags and arguments. More details about this script and other installation methods can be found `in the User Guide -`_. +`_. How to run the client --------------------- -In many cases, you can just run ``letsencrypt-auto`` or ``letsencrypt``, and the +In many cases, you can just run ``certbot-auto`` or ``certbot``, and the client will guide you through the process of obtaining and installing certs interactively. @@ -56,7 +76,7 @@ For instance, if you want to obtain a cert for ``example.com``, ``www.example.com``, and ``other.example.net``, using the Apache plugin to both obtain and install the certs, you could do this:: - ./letsencrypt-auto --apache -d example.com -d www.example.com -d other.example.net + ./certbot-auto --apache -d example.com -d www.example.com -d other.example.net (The first time you run the command, it will make an account, and ask for an email and agreement to the Let's Encrypt Subscriber Agreement; you can @@ -65,7 +85,7 @@ automate those with ``--email`` and ``--agree-tos``) If you want to use a webserver that doesn't have full plugin support yet, you can still use "standalone" or "webroot" plugins to obtain a certificate:: - ./letsencrypt-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net + ./certbot-auto certonly --standalone --email admin@example.com -d example.com -d www.example.com -d other.example.net Understanding the client in more depth @@ -73,21 +93,21 @@ Understanding the client in more depth To understand what the client is doing in detail, it's important to understand the way it uses plugins. Please see the `explanation of -plugins `_ in +plugins `_ in the User Guide. Links ===== -Documentation: https://letsencrypt.readthedocs.org +Documentation: https://certbot.eff.org/docs -Software project: https://github.com/letsencrypt/letsencrypt +Software project: https://github.com/certbot/certbot -Notes for developers: https://letsencrypt.readthedocs.org/en/latest/contributing.html +Notes for developers: https://certbot.eff.org/docs/contributing.html Main Website: https://letsencrypt.org/ -IRC Channel: #letsencrypt on `Freenode`_ +IRC Channel: #letsencrypt on `Freenode`_ or #certbot on `OFTC`_ Community: https://community.letsencrypt.org @@ -103,12 +123,12 @@ email to client-dev+subscribe@letsencrypt.org) -.. |build-status| image:: https://travis-ci.org/letsencrypt/letsencrypt.svg?branch=master - :target: https://travis-ci.org/letsencrypt/letsencrypt +.. |build-status| image:: https://travis-ci.org/certbot/certbot.svg?branch=master + :target: https://travis-ci.org/certbot/certbot :alt: Travis CI status -.. |coverage| image:: https://coveralls.io/repos/letsencrypt/letsencrypt/badge.svg?branch=master - :target: https://coveralls.io/r/letsencrypt/letsencrypt +.. |coverage| image:: https://coveralls.io/repos/certbot/certbot/badge.svg?branch=master + :target: https://coveralls.io/r/certbot/certbot :alt: Coverage status .. |docs| image:: https://readthedocs.org/projects/letsencrypt/badge/ @@ -151,10 +171,10 @@ Current Features - standalone (runs its own simple webserver to prove you control a domain) - webroot (adds files to webroot directories in order to prove control of domains and obtain certs) - - nginx/0.8.48+ (highly experimental, not included in letsencrypt-auto) + - nginx/0.8.48+ (highly experimental, not included in certbot-auto) * The private key is generated locally on your system. -* Can talk to the Let's Encrypt CA or optionally to other ACME +* Can talk to the Let's Encrypt CA or optionally to other ACME compliant services. * Can get domain-validated (DV) certificates. * Can revoke certificates. @@ -169,4 +189,6 @@ Current Features .. _Freenode: https://webchat.freenode.net?channels=%23letsencrypt +.. _OFTC: https://webchat.oftc.net?channels=%23certbot .. _client-dev: https://groups.google.com/a/letsencrypt.org/forum/#!forum/client-dev +.. _certbot.eff.org: https://certbot.eff.org/ diff --git a/acme/acme/client.py b/acme/acme/client.py index 225245686..117ee6b7d 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -512,6 +512,10 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes self.verify_ssl = verify_ssl self._nonces = set() self.user_agent = user_agent + self.session = requests.Session() + + def __del__(self): + self.session.close() def _wrap_in_jws(self, obj, nonce): """Wrap `JSONDeSerializable` object in JWS. @@ -606,7 +610,7 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes kwargs['verify'] = self.verify_ssl kwargs.setdefault('headers', {}) kwargs['headers'].setdefault('User-Agent', self.user_agent) - response = requests.request(method, url, *args, **kwargs) + response = self.session.request(method, url, *args, **kwargs) logging.debug('Received %s. Headers: %s. Content: %r', response, response.headers, response.content) return response diff --git a/acme/acme/client_test.py b/acme/acme/client_test.py index 7403cde81..33e80aab7 100644 --- a/acme/acme/client_test.py +++ b/acme/acme/client_test.py @@ -530,40 +530,49 @@ class ClientNetworkTest(unittest.TestCase): self.assertEqual( self.response, self.net._check_response(self.response)) - @mock.patch('acme.client.requests') - def test_send_request(self, mock_requests): - mock_requests.request.return_value = self.response + def test_send_request(self): + self.net.session = mock.MagicMock() + self.net.session.request.return_value = self.response # pylint: disable=protected-access self.assertEqual(self.response, self.net._send_request( - 'HEAD', 'url', 'foo', bar='baz')) - mock_requests.request.assert_called_once_with( - 'HEAD', 'url', 'foo', verify=mock.ANY, bar='baz', headers=mock.ANY) + 'HEAD', 'http://example.com/', 'foo', bar='baz')) + self.net.session.request.assert_called_once_with( + 'HEAD', 'http://example.com/', 'foo', + headers=mock.ANY, verify=mock.ANY, bar='baz') - @mock.patch('acme.client.requests') - def test_send_request_verify_ssl(self, mock_requests): + def test_send_request_verify_ssl(self): # pylint: disable=protected-access for verify in True, False: - mock_requests.request.reset_mock() - mock_requests.request.return_value = self.response + self.net.session = mock.MagicMock() + self.net.session.request.return_value = self.response self.net.verify_ssl = verify # pylint: disable=protected-access self.assertEqual( - self.response, self.net._send_request('GET', 'url')) - mock_requests.request.assert_called_once_with( - 'GET', 'url', verify=verify, headers=mock.ANY) + self.response, + self.net._send_request('GET', 'http://example.com/')) + self.net.session.request.assert_called_once_with( + 'GET', 'http://example.com/', verify=verify, headers=mock.ANY) - @mock.patch('acme.client.requests') - def test_send_request_user_agent(self, mock_requests): - mock_requests.request.return_value = self.response + def test_send_request_user_agent(self): + self.net.session = mock.MagicMock() # pylint: disable=protected-access - self.net._send_request('GET', 'url', headers={'bar': 'baz'}) - mock_requests.request.assert_called_once_with( - 'GET', 'url', verify=mock.ANY, + self.net._send_request('GET', 'http://example.com/', + headers={'bar': 'baz'}) + self.net.session.request.assert_called_once_with( + 'GET', 'http://example.com/', verify=mock.ANY, headers={'User-Agent': 'acme-python-test', 'bar': 'baz'}) - self.net._send_request('GET', 'url', headers={'User-Agent': 'foo2'}) - mock_requests.request.assert_called_with( - 'GET', 'url', verify=mock.ANY, headers={'User-Agent': 'foo2'}) + self.net._send_request('GET', 'http://example.com/', + headers={'User-Agent': 'foo2'}) + self.net.session.request.assert_called_with( + 'GET', 'http://example.com/', + verify=mock.ANY, headers={'User-Agent': 'foo2'}) + + def test_del(self): + sess = mock.MagicMock() + self.net.session = sess + del self.net + sess.close.assert_called_once() @mock.patch('acme.client.requests') def test_requests_error_passthrough(self, mock_requests): @@ -616,14 +625,16 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase): return self.checked_response def test_head(self): - self.assertEqual(self.response, self.net.head('url', 'foo', bar='baz')) + self.assertEqual(self.response, self.net.head( + 'http://example.com/', 'foo', bar='baz')) self.send_request.assert_called_once_with( - 'HEAD', 'url', 'foo', bar='baz') + 'HEAD', 'http://example.com/', 'foo', bar='baz') def test_get(self): self.assertEqual(self.checked_response, self.net.get( - 'url', content_type=self.content_type, bar='baz')) - self.send_request.assert_called_once_with('GET', 'url', bar='baz') + 'http://example.com/', content_type=self.content_type, bar='baz')) + self.send_request.assert_called_once_with( + 'GET', 'http://example.com/', bar='baz') def test_post(self): # pylint: disable=protected-access diff --git a/acme/setup.py b/acme/setup.py index cbd3bfb87..d38864dc1 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.7.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-apache/certbot_apache/configurator.py b/certbot-apache/certbot_apache/configurator.py index 26c3185be..1e02ae7b3 100644 --- a/certbot-apache/certbot_apache/configurator.py +++ b/certbot-apache/certbot_apache/configurator.py @@ -124,13 +124,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.assoc = dict() # Outstanding challenges self._chall_out = set() + # Maps enhancements to vhosts we've enabled the enhancement for + self._enhanced_vhosts = defaultdict(set) # These will be set in the prepare function self.parser = None 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, + "staple-ocsp": self._enable_ocsp_stapling} @property def mod_ssl_conf(self): @@ -593,8 +596,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :type addr: :class:`~certbot_apache.obj.Addr` """ - loc = parser.get_aug_path(self.parser.loc["name"]) + loc = parser.get_aug_path(self.parser.loc["name"]) if addr.get_port() == "443": path = self.parser.add_dir_to_ifmodssl( loc, "NameVirtualHost", [str(addr)]) @@ -944,7 +947,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): ###################################################################### def supported_enhancements(self): # pylint: disable=no-self-use """Returns currently supported enhancements.""" - return ["redirect", "ensure-http-header"] + return ["redirect", "ensure-http-header", "staple-ocsp"] def enhance(self, domain, enhancement, options=None): """Enhance configuration. @@ -971,6 +974,68 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): logger.warn("Failed %s for %s", enhancement, domain) raise + def _enable_ocsp_stapling(self, ssl_vhost, unused_options): + """Enables OCSP Stapling + + In OCSP, each client (e.g. browser) would have to query the + OCSP Responder to validate that the site certificate was not revoked. + + Enabling OCSP Stapling, would allow the web-server to query the OCSP + Responder, and staple its response to the offered certificate during + TLS. i.e. clients would not have to query the OCSP responder. + + OCSP Stapling enablement on Apache implicitly depends on + SSLCertificateChainFile being set by other code. + + .. note:: This function saves the configuration + + :param ssl_vhost: Destination of traffic, an ssl enabled vhost + :type ssl_vhost: :class:`~letsencrypt_apache.obj.VirtualHost` + + :param unused_options: Not currently used + :type unused_options: Not Available + + :returns: Success, general_vhost (HTTP vhost) + :rtype: (bool, :class:`~letsencrypt_apache.obj.VirtualHost`) + + """ + min_apache_ver = (2, 3, 3) + if self.get_version() < min_apache_ver: + raise errors.PluginError( + "Unable to set OCSP directives.\n" + "Apache version is below 2.3.3.") + + if "socache_shmcb_module" not in self.parser.modules: + self.enable_mod("socache_shmcb") + + # Check if there's an existing SSLUseStapling directive on. + use_stapling_aug_path = self.parser.find_dir("SSLUseStapling", + "on", start=ssl_vhost.path) + if not use_stapling_aug_path: + self.parser.add_dir(ssl_vhost.path, "SSLUseStapling", "on") + + ssl_vhost_aug_path = parser.get_aug_path(ssl_vhost.filep) + + # Check if there's an existing SSLStaplingCache directive. + stapling_cache_aug_path = self.parser.find_dir('SSLStaplingCache', + None, ssl_vhost_aug_path) + + # We'll simply delete the directive, so that we'll have a + # consistent OCSP cache path. + if stapling_cache_aug_path: + self.aug.remove( + re.sub(r"/\w*$", "", stapling_cache_aug_path[0])) + + self.parser.add_dir_to_ifmodssl(ssl_vhost_aug_path, + "SSLStaplingCache", + ["shmcb:/var/run/apache2/stapling_cache(128000)"]) + + msg = "OCSP Stapling was enabled on SSL Vhost: %s.\n"%( + ssl_vhost.filep) + self.save_notes += msg + self.save() + logger.info(msg) + def _set_http_header(self, ssl_vhost, header_substring): """Enables header that is identified by header_substring on ssl_vhost. @@ -1058,9 +1123,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :param unused_options: Not currently used :type unused_options: Not Available - :returns: Success, general_vhost (HTTP vhost) - :rtype: (bool, :class:`~certbot_apache.obj.VirtualHost`) - :raises .errors.PluginError: If no viable HTTP host can be created or used for the redirect. @@ -1083,6 +1145,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): "redirection") self._create_redirect_vhost(ssl_vhost) else: + if general_vh in self._enhanced_vhosts["redirect"]: + logger.debug("Already enabled redirect for this vhost") + return + # Check if Certbot redirection already exists self._verify_no_certbot_redirect(general_vh) @@ -1118,6 +1184,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): (general_vh.filep, ssl_vhost.filep)) self.save() + self._enhanced_vhosts["redirect"].add(general_vh) logger.info("Redirecting vhost in %s to ssl vhost in %s", general_vh.filep, ssl_vhost.filep) @@ -1177,10 +1244,14 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): :type vhost: :class:`~certbot_apache.obj.VirtualHost` """ - rewrite_engine_path = self.parser.find_dir("RewriteEngine", "on", + rewrite_engine_path_list = self.parser.find_dir("RewriteEngine", "on", start=vhost.path) - if rewrite_engine_path: - return self.parser.get_arg(rewrite_engine_path[0]) + if rewrite_engine_path_list: + for re_path in rewrite_engine_path_list: + # A RewriteEngine directive may also be included in per + # directory .htaccess files. We only care about the VirtualHost. + if 'VirtualHost' in re_path: + return self.parser.get_arg(re_path) return False def _create_redirect_vhost(self, ssl_vhost): @@ -1202,6 +1273,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Make a new vhost data structure and add it to the lists new_vhost = self._create_vhost(parser.get_aug_path(redirect_filepath)) self.vhosts.append(new_vhost) + self._enhanced_vhosts["redirect"].add(new_vhost) # Finally create documentation for the change self.save_notes += ("Created a port 80 vhost, %s, for redirection to " diff --git a/certbot-apache/certbot_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py index f2f78c8f9..d7a5fd75a 100644 --- a/certbot-apache/certbot_apache/tests/configurator_test.py +++ b/certbot-apache/certbot_apache/tests/configurator_test.py @@ -15,6 +15,7 @@ from certbot import errors from certbot.tests import acme_util from certbot_apache import configurator +from certbot_apache import parser from certbot_apache import obj from certbot_apache.tests import util @@ -85,7 +86,8 @@ class MultipleVhostsTest(util.ApacheTest): mock_getutility.notification = mock.MagicMock(return_value=True) names = self.config.get_all_names() self.assertEqual(names, set( - ["certbot.demo", "encryption-example.demo", "ip-172-30-0-17", "*.blue.purple.com"])) + ["certbot.demo", "ocspvhost.com", "encryption-example.demo", + "ip-172-30-0-17", "*.blue.purple.com"])) @mock.patch("zope.component.getUtility") @mock.patch("certbot_apache.configurator.socket.gethostbyaddr") @@ -100,14 +102,24 @@ class MultipleVhostsTest(util.ApacheTest): obj.Addr(("zombo.com",)), obj.Addr(("192.168.1.2"))]), True, False) + self.config.vhosts.append(vhost) names = self.config.get_all_names() - self.assertEqual(len(names), 6) + self.assertEqual(len(names), 7) self.assertTrue("zombo.com" in names) self.assertTrue("google.com" in names) self.assertTrue("certbot.demo" in names) + def test_bad_servername_alias(self): + ssl_vh1 = obj.VirtualHost( + "fp1", "ap1", set([obj.Addr(("*", "443"))]), + True, False) + # pylint: disable=protected-access + self.config._add_servernames(ssl_vh1) + self.assertTrue( + self.config._add_servername_alias("oy_vey", ssl_vh1) is None) + def test_add_servernames_alias(self): self.config.parser.add_dir( self.vh_truth[2].path, "ServerAlias", ["*.le.co"]) @@ -124,7 +136,7 @@ class MultipleVhostsTest(util.ApacheTest): """ vhs = self.config.get_virtual_hosts() - self.assertEqual(len(vhs), 7) + self.assertEqual(len(vhs), 8) found = 0 for vhost in vhs: @@ -135,7 +147,7 @@ class MultipleVhostsTest(util.ApacheTest): else: raise Exception("Missed: %s" % vhost) # pragma: no cover - self.assertEqual(found, 7) + self.assertEqual(found, 8) # Handle case of non-debian layout get_virtual_hosts with mock.patch( @@ -143,7 +155,7 @@ class MultipleVhostsTest(util.ApacheTest): ) as mock_conf: mock_conf.return_value = False vhs = self.config.get_virtual_hosts() - self.assertEqual(len(vhs), 7) + self.assertEqual(len(vhs), 8) @mock.patch("certbot_apache.display_ops.select_vhost") def test_choose_vhost_none_avail(self, mock_select): @@ -224,16 +236,18 @@ class MultipleVhostsTest(util.ApacheTest): # Assume only the two default vhosts. self.config.vhosts = [ vh for vh in self.config.vhosts - if vh.name not in ["certbot.demo", "encryption-example.demo"] + if vh.name not in ["certbot.demo", + "encryption-example.demo", + "ocspvhost.com"] and "*.blue.purple.com" not in vh.aliases ] - self.assertEqual( - self.config._find_best_vhost("example.demo"), self.vh_truth[2]) + self.config._find_best_vhost("encryption-example.demo"), + self.vh_truth[2]) def test_non_default_vhosts(self): # pylint: disable=protected-access - self.assertEqual(len(self.config._non_default_vhosts()), 5) + self.assertEqual(len(self.config._non_default_vhosts()), 6) def test_is_site_enabled(self): """Test if site is enabled. @@ -539,7 +553,7 @@ class MultipleVhostsTest(util.ApacheTest): self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]), self.config.is_name_vhost(ssl_vhost)) - self.assertEqual(len(self.config.vhosts), 8) + self.assertEqual(len(self.config.vhosts), 9) def test_clean_vhost_ssl(self): # pylint: disable=protected-access @@ -726,16 +740,15 @@ class MultipleVhostsTest(util.ApacheTest): def test_get_all_certs_keys(self): c_k = self.config.get_all_certs_keys() - - self.assertEqual(len(c_k), 2) + self.assertEqual(len(c_k), 3) cert, key, path = next(iter(c_k)) self.assertTrue("cert" in cert) self.assertTrue("key" in key) - self.assertTrue("default-ssl" in path) + self.assertTrue("default-ssl" in path or "ocsp-ssl" in path) def test_get_all_certs_keys_malformed_conf(self): self.config.parser.find_dir = mock.Mock( - side_effect=[["path"], [], ["path"], []]) + side_effect=[["path"], [], ["path"], [], ["path"], []]) c_k = self.config.get_all_certs_keys() self.assertFalse(c_k) @@ -756,15 +769,20 @@ class MultipleVhostsTest(util.ApacheTest): def test_supported_enhancements(self): self.assertTrue(isinstance(self.config.supported_enhancements(), list)) + @mock.patch("certbot_apache.configurator.ApacheConfigurator._get_http_vhost") + @mock.patch("certbot_apache.display_ops.select_vhost") @mock.patch("certbot.le_util.exe_exists") - def test_enhance_unknown_vhost(self, mock_exe): + def test_enhance_unknown_vhost(self, mock_exe, mock_sel_vhost, mock_get): self.config.parser.modules.add("rewrite_module") mock_exe.return_value = True - ssl_vh = obj.VirtualHost( - "fp", "ap", set([obj.Addr(("*", "443"))]), + ssl_vh1 = obj.VirtualHost( + "fp1", "ap1", set([obj.Addr(("*", "443"))]), True, False) - ssl_vh.name = "satoshi.com" - self.config.vhosts.append(ssl_vh) + ssl_vh1.name = "satoshi.com" + self.config.vhosts.append(ssl_vh1) + mock_sel_vhost.return_value = None + mock_get.return_value = None + self.assertRaises( errors.PluginError, self.config.enhance, "satoshi.com", "redirect") @@ -774,6 +792,85 @@ class MultipleVhostsTest(util.ApacheTest): errors.PluginError, self.config.enhance, "certbot.demo", "unknown_enhancement") + @mock.patch("certbot.le_util.run_script") + @mock.patch("certbot.le_util.exe_exists") + def test_ocsp_stapling(self, mock_exe, mock_run_script): + self.config.parser.update_runtime_variables = mock.Mock() + self.config.parser.modules.add("mod_ssl.c") + self.config.get_version = mock.Mock(return_value=(2, 4, 7)) + mock_exe.return_value = True + + # This will create an ssl vhost for certbot.demo + self.config.enhance("certbot.demo", "staple-ocsp") + + self.assertTrue("socache_shmcb_module" in self.config.parser.modules) + self.assertTrue(mock_run_script.called) + + # Get the ssl vhost for certbot.demo + ssl_vhost = self.config.assoc["certbot.demo"] + + ssl_use_stapling_aug_path = self.config.parser.find_dir( + "SSLUseStapling", "on", ssl_vhost.path) + + self.assertEqual(len(ssl_use_stapling_aug_path), 1) + + ssl_vhost_aug_path = parser.get_aug_path(ssl_vhost.filep) + stapling_cache_aug_path = self.config.parser.find_dir('SSLStaplingCache', + "shmcb:/var/run/apache2/stapling_cache(128000)", + ssl_vhost_aug_path) + + self.assertEqual(len(stapling_cache_aug_path), 1) + + @mock.patch("certbot.le_util.exe_exists") + def test_ocsp_stapling_twice(self, mock_exe): + self.config.parser.update_runtime_variables = mock.Mock() + self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("socache_shmcb_module") + self.config.get_version = mock.Mock(return_value=(2, 4, 7)) + mock_exe.return_value = True + + # Checking the case with already enabled ocsp stapling configuration + self.config.enhance("ocspvhost.com", "staple-ocsp") + + # Get the ssl vhost for letsencrypt.demo + ssl_vhost = self.config.assoc["ocspvhost.com"] + + ssl_use_stapling_aug_path = self.config.parser.find_dir( + "SSLUseStapling", "on", ssl_vhost.path) + + self.assertEqual(len(ssl_use_stapling_aug_path), 1) + + ssl_vhost_aug_path = parser.get_aug_path(ssl_vhost.filep) + stapling_cache_aug_path = self.config.parser.find_dir('SSLStaplingCache', + "shmcb:/var/run/apache2/stapling_cache(128000)", + ssl_vhost_aug_path) + + self.assertEqual(len(stapling_cache_aug_path), 1) + + + @mock.patch("certbot.le_util.exe_exists") + def test_ocsp_unsupported_apache_version(self, mock_exe): + mock_exe.return_value = True + self.config.parser.update_runtime_variables = mock.Mock() + self.config.parser.modules.add("mod_ssl.c") + self.config.parser.modules.add("socache_shmcb_module") + self.config.get_version = mock.Mock(return_value=(2, 2, 0)) + + self.assertRaises(errors.PluginError, + self.config.enhance, "certbot.demo", "staple-ocsp") + + + def test_get_http_vhost_third_filter(self): + ssl_vh = obj.VirtualHost( + "fp", "ap", set([obj.Addr(("*", "443"))]), + True, False) + ssl_vh.name = "satoshi.com" + self.config.vhosts.append(ssl_vh) + + # pylint: disable=protected-access + http_vh = self.config._get_http_vhost(ssl_vh) + self.assertTrue(http_vh.ssl == False) + @mock.patch("certbot.le_util.run_script") @mock.patch("certbot.le_util.exe_exists") def test_http_header_hsts(self, mock_exe, _): @@ -899,7 +996,7 @@ class MultipleVhostsTest(util.ApacheTest): def test_redirect_with_existing_rewrite(self, mock_exe, _): self.config.parser.update_runtime_variables = mock.Mock() mock_exe.return_value = True - self.config.get_version = mock.Mock(return_value=(2, 2)) + self.config.get_version = mock.Mock(return_value=(2, 2, 0)) # Create a preexisting rewrite rule self.config.parser.add_dir( @@ -938,15 +1035,31 @@ class MultipleVhostsTest(util.ApacheTest): self.assertRaises( errors.PluginError, self.config._enable_redirect, ssl_vh, "") - def test_redirect_twice(self): + def test_redirect_two_domains_one_vhost(self): # Skip the enable mod self.config.parser.modules.add("rewrite_module") self.config.get_version = mock.Mock(return_value=(2, 3, 9)) - self.config.enhance("encryption-example.demo", "redirect") + self.config.enhance("red.blue.purple.com", "redirect") + verify_no_redirect = ("certbot_apache.configurator." + "ApacheConfigurator._verify_no_certbot_redirect") + with mock.patch(verify_no_redirect) as mock_verify: + self.config.enhance("green.blue.purple.com", "redirect") + self.assertFalse(mock_verify.called) + + def test_redirect_from_previous_run(self): + # Skip the enable mod + self.config.parser.modules.add("rewrite_module") + self.config.get_version = mock.Mock(return_value=(2, 3, 9)) + + self.config.enhance("red.blue.purple.com", "redirect") + # Clear state about enabling redirect on this run + # pylint: disable=protected-access + self.config._enhanced_vhosts["redirect"].clear() + self.assertRaises( errors.PluginEnhancementAlreadyPresent, - self.config.enhance, "encryption-example.demo", "redirect") + self.config.enhance, "green.blue.purple.com", "redirect") def test_create_own_redirect(self): self.config.parser.modules.add("rewrite_module") @@ -957,7 +1070,7 @@ class MultipleVhostsTest(util.ApacheTest): # pylint: disable=protected-access self.config._enable_redirect(self.vh_truth[1], "") - self.assertEqual(len(self.config.vhosts), 8) + self.assertEqual(len(self.config.vhosts), 9) def test_create_own_redirect_for_old_apache_version(self): self.config.parser.modules.add("rewrite_module") @@ -968,7 +1081,7 @@ class MultipleVhostsTest(util.ApacheTest): # pylint: disable=protected-access self.config._enable_redirect(self.vh_truth[1], "") - self.assertEqual(len(self.config.vhosts), 8) + self.assertEqual(len(self.config.vhosts), 9) def test_sift_line(self): # pylint: disable=protected-access diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/ocsp-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/ocsp-ssl.conf new file mode 100644 index 000000000..631cf16c8 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-available/ocsp-ssl.conf @@ -0,0 +1,36 @@ + +SSLStaplingCache shmcb:/var/run/apache2/stapling_cache(128000) + + # The ServerName directive sets the request scheme, hostname and port that + # the server uses to identify itself. This is used when creating + # redirection URLs. In the context of virtual hosts, the ServerName + # specifies what hostname must appear in the request's Host: header to + # match this virtual host. For the default virtual host (this file) this + # value is not decisive as it is used as a last resort host regardless. + # However, you must set it for any further virtual host explicitly. + ServerName ocspvhost.com + + ServerAdmin webmaster@dumpbits.com + DocumentRoot /var/www/html + + # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, + # error, crit, alert, emerg. + # It is also possible to configure the loglevel for particular + # modules, e.g. + #LogLevel info ssl:warn + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + + # For most configuration files from conf-available/, which are + # enabled or disabled at a global level, it is possible to + # include a line for only one particular virtual host. For example the + # following line enables the CGI configuration for this host only + # after it has been globally disabled with "a2disconf". + #Include conf-available/serve-cgi-bin.conf +SSLCertificateFile /etc/apache2/certs/certbot-cert_5.pem +SSLCertificateKeyFile /etc/apache2/ssl/key-certbot_15.pem +SSLUseStapling on + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet + diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/ocsp-ssl.conf b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/ocsp-ssl.conf new file mode 120000 index 000000000..b25ee0482 --- /dev/null +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/apache2/sites-enabled/ocsp-ssl.conf @@ -0,0 +1 @@ +../sites-available/ocsp-ssl.conf \ No newline at end of file diff --git a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites index 06bf6a2ae..ab518ee5b 100644 --- a/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites +++ b/certbot-apache/certbot_apache/tests/testdata/debian_apache_2_4/multiple_vhosts/sites @@ -1,2 +1,3 @@ sites-available/certbot.conf, certbot.demo sites-available/encryption-example.conf, encryption-example.demo +sites-available/ocsp-ssl.conf, ocspvhost.com diff --git a/certbot-apache/certbot_apache/tests/util.py b/certbot-apache/certbot_apache/tests/util.py index 9fb5dcdfa..8935ee908 100644 --- a/certbot-apache/certbot_apache/tests/util.py +++ b/certbot-apache/certbot_apache/tests/util.py @@ -156,8 +156,12 @@ def get_vh_truth(temp_dir, config_name): os.path.join(prefix, "wildcard.conf"), os.path.join(aug_pre, "wildcard.conf/VirtualHost"), set([obj.Addr.fromstring("*:80")]), False, False, - "ip-172-30-0-17", aliases=["*.blue.purple.com"]) - ] + "ip-172-30-0-17", aliases=["*.blue.purple.com"]), + obj.VirtualHost( + os.path.join(prefix, "ocsp-ssl.conf"), + os.path.join(aug_pre, "ocsp-ssl.conf/IfModule/VirtualHost"), + set([obj.Addr.fromstring("10.2.3.4:443")]), True, True, + "ocspvhost.com")] return vh_truth return None # pragma: no cover diff --git a/certbot-apache/docs/conf.py b/certbot-apache/docs/conf.py index 2f996c7f4..d2fe15581 100644 --- a/certbot-apache/docs/conf.py +++ b/certbot-apache/docs/conf.py @@ -314,5 +314,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), } diff --git a/certbot-apache/setup.py b/certbot-apache/setup.py index 7358c7041..56c6a451d 100644 --- a/certbot-apache/setup.py +++ b/certbot-apache/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.7.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot-auto b/certbot-auto new file mode 100755 index 000000000..8c6e6c486 --- /dev/null +++ b/certbot-auto @@ -0,0 +1,1088 @@ +#!/bin/sh +# +# Download and run the latest release version of the Certbot client. +# +# NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING +# +# IF YOU WANT TO EDIT IT LOCALLY, *ALWAYS* RUN YOUR COPY WITH THE +# "--no-self-upgrade" FLAG +# +# IF YOU WANT TO SEND PULL REQUESTS, THE REAL SOURCE FOR THIS FILE IS +# letsencrypt-auto-source/letsencrypt-auto.template AND +# letsencrypt-auto-source/pieces/bootstrappers/* + +set -e # Work even if somebody does "sh thisscript.sh". + +# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script, +# if you want to change where the virtual environment will be installed +XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} +VENV_NAME="letsencrypt" +VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} +VENV_BIN="$VENV_PATH/bin" +LE_AUTO_VERSION="0.6.0" +BASENAME=$(basename $0) +USAGE="Usage: $BASENAME [OPTIONS] +A self-updating wrapper script for the Certbot ACME client. When run, updates +to both this script and certbot will be downloaded and installed. After +ensuring you have the latest versions installed, certbot will be invoked with +all arguments you have provided. + +Help for certbot itself cannot be provided until it is installed. + + --debug attempt experimental installation + -h, --help print this help + -n, --non-interactive, --noninteractive run without asking for user input + --no-self-upgrade do not download updates + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output + +All arguments are accepted and forwarded to the Certbot client when run." + +while getopts ":hnv" arg; do + case $arg in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac +done + +for arg in "$@" ; do + case "$arg" in + --debug) + DEBUG=1;; + --os-packages-only) + OS_PACKAGES_ONLY=1;; + --no-self-upgrade) + # Do not upgrade this script (also prevents client upgrades, because each + # copy of the script pins a hash of the python client) + NO_SELF_UPGRADE=1;; + --help) + HELP=1;; + --noninteractive|--non-interactive) + ASSUME_YES=1;; + --verbose) + VERBOSE=1;; + esac +done + +# certbot-auto needs root access to bootstrap OS dependencies, and +# certbot itself needs root access for almost all modes of operation +# The "normal" case is that sudo is used for the steps that need root, but +# this script *can* be run as root (not recommended), or fall back to using +# `su` +SUDO_ENV="" +export CERTBOT_AUTO="$0" +if test "`id -u`" -ne "0" ; then + if command -v sudo 1>/dev/null 2>&1; then + SUDO=sudo + SUDO_ENV="CERTBOT_AUTO=$0" + else + echo \"sudo\" is not available, will use \"su\" for installation steps... + # Because the parameters in `su -c` has to be a string, + # we need properly escape it + su_sudo() { + args="" + # This `while` loop iterates over all parameters given to this function. + # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string + # will be wrapped in a pair of `'`, then appended to `$args` string + # For example, `echo "It's only 1\$\!"` will be escaped to: + # 'echo' 'It'"'"'s only 1$!' + # │ │└┼┘│ + # │ │ │ └── `'s only 1$!'` the literal string + # │ │ └── `\"'\"` is a single quote (as a string) + # │ └── `'It'`, to be concatenated with the strings following it + # └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself + while [ $# -ne 0 ]; do + args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' " + shift + done + su root -c "$args" + } + SUDO=su_sudo + fi +else + SUDO= +fi + +if [ $BASENAME = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + +ExperimentalBootstrap() { + # Arguments: Platform name, bootstrap function name + if [ "$DEBUG" = 1 ]; then + if [ "$2" != "" ]; then + echo "Bootstrapping dependencies via $1..." + $2 + fi + else + echo "WARNING: $1 support is very experimental at present..." + echo "if you would like to work on improving it, please ensure you have backups" + echo "and then run this script again with the --debug flag!" + exit 1 + fi +} + +DeterminePythonVersion() { + for LE_PYTHON in "$LE_PYTHON" python2.7 python27 python2 python; do + # Break (while keeping the LE_PYTHON value) if found. + command -v "$LE_PYTHON" > /dev/null && break + done + if [ "$?" != "0" ]; then + echo "Cannot find any Pythons; please install one!" + exit 1 + fi + export LE_PYTHON + + PYVER=`"$LE_PYTHON" -V 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'` + if [ "$PYVER" -lt 26 ]; then + echo "You have an ancient version of Python entombed in your operating system..." + echo "This isn't going to work; you'll need at least version 2.6." + exit 1 + fi +} + +BootstrapDebCommon() { + # Current version tested with: + # + # - Ubuntu + # - 14.04 (x64) + # - 15.04 (x64) + # - Debian + # - 7.9 "wheezy" (x64) + # - sid (2015-10-21) (x64) + + # Past versions tested with: + # + # - Debian 8.0 "jessie" (x64) + # - Raspbian 7.8 (armhf) + + # Believed not to work: + # + # - Debian 6.0.10 "squeeze" (x64) + + $SUDO apt-get update || echo apt-get update hit problems but continuing anyway... + + # virtualenv binary can be found in different packages depending on + # distro version (#346) + + virtualenv= + if apt-cache show virtualenv > /dev/null 2>&1; then + virtualenv="virtualenv" + fi + + if apt-cache show python-virtualenv > /dev/null 2>&1; then + virtualenv="$virtualenv python-virtualenv" + fi + + augeas_pkg="libaugeas0 augeas-lenses" + AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + + if [ "$ASSUME_YES" = 1 ]; then + YES_FLAG="-y" + fi + + AddBackportRepo() { + # ARGS: + BACKPORT_NAME="$1" + BACKPORT_SOURCELINE="$2" + echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME." + if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then + # This can theoretically error if sources.list.d is empty, but in that case we don't care. + if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then + if [ "$ASSUME_YES" = 1 ]; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s + add_backports=1 + else + read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response + case $response in + [yY][eE][sS]|[yY]|"") + add_backports=1;; + *) + add_backports=0;; + esac + fi + if [ "$add_backports" = 1 ]; then + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update + fi + fi + fi + if [ "$add_backports" != 0 ]; then + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + fi + } + + + if dpkg --compare-versions 1.0 gt "$AUGVERSION" ; then + if lsb_release -a | grep -q wheezy ; then + AddBackportRepo wheezy-backports "deb http://http.debian.net/debian wheezy-backports main" + elif lsb_release -a | grep -q precise ; then + # XXX add ARM case + AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" + else + echo "No libaugeas0 version is available that's new enough to run the" + echo "Certbot apache plugin..." + fi + # XXX add a case for ubuntu PPAs + fi + + $SUDO apt-get install $YES_FLAG --no-install-recommends \ + python \ + python-dev \ + $virtualenv \ + gcc \ + dialog \ + $augeas_pkg \ + libssl-dev \ + libffi-dev \ + ca-certificates \ + + + + if ! command -v virtualenv > /dev/null ; then + echo Failed to install a working \"virtualenv\" command, exiting + exit 1 + fi +} + +BootstrapRpmCommon() { + # Tested with: + # - Fedora 20, 21, 22, 23 (x64) + # - Centos 7 (x64: on DigitalOcean droplet) + # - CentOS 7 Minimal install in a Hyper-V VM + # - CentOS 6 (EPEL must be installed manually) + + if type dnf 2>/dev/null + then + tool=dnf + elif type yum 2>/dev/null + then + tool=yum + + else + echo "Neither yum nor dnf found. Aborting bootstrap!" + exit 1 + fi + + pkgs=" + gcc + dialog + augeas-libs + openssl + openssl-devel + libffi-devel + redhat-rpm-config + ca-certificates + " + + # Some distros and older versions of current distros use a "python27" + # instead of "python" naming convention. Try both conventions. + if $SUDO $tool list python >/dev/null 2>&1; then + pkgs="$pkgs + python + python-devel + python-virtualenv + python-tools + python-pip + " + else + pkgs="$pkgs + python27 + python27-devel + python27-virtualenv + python27-tools + python27-pip + " + fi + + if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then + pkgs="$pkgs + mod_ssl + " + fi + + if [ "$ASSUME_YES" = 1 ]; then + yes_flag="-y" + fi + + if ! $SUDO $tool install $yes_flag $pkgs; then + echo "Could not install OS dependencies. Aborting bootstrap!" + exit 1 + fi +} + +BootstrapSuseCommon() { + # SLE12 don't have python-virtualenv + + if [ "$ASSUME_YES" = 1 ]; then + zypper_flags="-nq" + install_flags="-l" + fi + + $SUDO zypper $zypper_flags in $install_flags \ + python \ + python-devel \ + python-virtualenv \ + gcc \ + dialog \ + augeas-lenses \ + libopenssl-devel \ + libffi-devel \ + ca-certificates +} + +BootstrapArchCommon() { + # Tested with: + # - ArchLinux (x86_64) + # + # "python-virtualenv" is Python3, but "python2-virtualenv" provides + # only "virtualenv2" binary, not "virtualenv" necessary in + # ./tools/_venv_common.sh + + deps=" + python2 + python-virtualenv + gcc + dialog + augeas + openssl + libffi + ca-certificates + pkg-config + " + + # pacman -T exits with 127 if there are missing dependencies + missing=$($SUDO pacman -T $deps) || true + + if [ "$ASSUME_YES" = 1 ]; then + noconfirm="--noconfirm" + fi + + if [ "$missing" ]; then + $SUDO pacman -S --needed $missing $noconfirm + fi +} + +BootstrapGentooCommon() { + PACKAGES=" + dev-lang/python:2.7 + dev-python/virtualenv + dev-util/dialog + app-admin/augeas + dev-libs/openssl + dev-libs/libffi + app-misc/ca-certificates + virtual/pkgconfig" + + case "$PACKAGE_MANAGER" in + (paludis) + $SUDO cave resolve --preserve-world --keep-targets if-possible $PACKAGES -x + ;; + (pkgcore) + $SUDO pmerge --noreplace --oneshot $PACKAGES + ;; + (portage|*) + $SUDO emerge --noreplace --oneshot $PACKAGES + ;; + esac +} + +BootstrapFreeBsd() { + $SUDO pkg install -Ay \ + python \ + py27-virtualenv \ + augeas \ + libffi +} + +BootstrapMac() { + if hash brew 2>/dev/null; then + echo "Using Homebrew to install dependencies..." + pkgman=brew + pkgcmd="brew install" + elif hash port 2>/dev/null; then + echo "Using MacPorts to install dependencies..." + pkgman=port + pkgcmd="$SUDO port install" + else + echo "No Homebrew/MacPorts; installing Homebrew..." + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + pkgman=brew + pkgcmd="brew install" + fi + + $pkgcmd augeas + $pkgcmd dialog + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + # We want to avoid using the system Python because it requires root to use pip. + # python.org, MacPorts or HomeBrew Python installations should all be OK. + echo "Installing python..." + $pkgcmd python + fi + + # Workaround for _dlopen not finding augeas on OS X + if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then + echo "Applying augeas workaround" + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib + fi + + if ! hash pip 2>/dev/null; then + echo "pip not installed" + echo "Installing pip..." + curl --silent --show-error --retry 5 https://bootstrap.pypa.io/get-pip.py | python + fi + + if ! hash virtualenv 2>/dev/null; then + echo "virtualenv not installed." + echo "Installing with pip..." + pip install virtualenv + fi +} + + +# Install required OS packages: +Bootstrap() { + if [ -f /etc/debian_version ]; then + echo "Bootstrapping dependencies for Debian-based OSes..." + BootstrapDebCommon + elif [ -f /etc/redhat-release ]; then + echo "Bootstrapping dependencies for RedHat-based OSes..." + BootstrapRpmCommon + elif [ -f /etc/os-release ] && `grep -q openSUSE /etc/os-release` ; then + echo "Bootstrapping dependencies for openSUSE-based OSes..." + BootstrapSuseCommon + elif [ -f /etc/arch-release ]; then + if [ "$DEBUG" = 1 ]; then + echo "Bootstrapping dependencies for Archlinux..." + BootstrapArchCommon + else + echo "Please use pacman to install letsencrypt packages:" + echo "# pacman -S letsencrypt letsencrypt-apache" + echo + echo "If you would like to use the virtualenv way, please run the script again with the" + echo "--debug flag." + exit 1 + fi + elif [ -f /etc/manjaro-release ]; then + ExperimentalBootstrap "Manjaro Linux" BootstrapArchCommon + elif [ -f /etc/gentoo-release ]; then + ExperimentalBootstrap "Gentoo" BootstrapGentooCommon + elif uname | grep -iq FreeBSD ; then + ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd + elif uname | grep -iq Darwin ; then + ExperimentalBootstrap "Mac OS X" BootstrapMac + elif grep -iq "Amazon Linux" /etc/issue ; then + ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon + else + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" + echo + echo "You will need to bootstrap, configure virtualenv, and run pip install manually." + echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" + echo "for more info." + fi +} + +TempDir() { + mktemp -d 2>/dev/null || mktemp -d -t 'le' # Linux || OS X +} + + + +if [ "$1" = "--le-auto-phase2" ]; then + # Phase 2: Create venv, install LE, and run. + + shift 1 # the --le-auto-phase2 arg + if [ -f "$VENV_BIN/letsencrypt" ]; then + # --version output ran through grep due to python-cryptography DeprecationWarnings + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) + else + INSTALLED_VERSION="none" + fi + if [ "$LE_AUTO_VERSION" != "$INSTALLED_VERSION" ]; then + echo "Creating virtual environment..." + DeterminePythonVersion + rm -rf "$VENV_PATH" + if [ "$VERBOSE" = 1 ]; then + virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" + else + virtualenv --no-site-packages --python "$LE_PYTHON" "$VENV_PATH" > /dev/null + fi + + echo "Installing Python packages..." + TEMP_DIR=$(TempDir) + # There is no $ interpolation due to quotes on starting heredoc delimiter. + # ------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" +# This is the flattened list of packages certbot-auto installs. To generate +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and +# then use `hashin` or a more secure method to gather the hashes. + +argparse==1.4.0 \ + --hash=sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314 \ + --hash=sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4 + +# This comes before cffi because cffi will otherwise install an unchecked +# version via setup_requires. +pycparser==2.14 \ + --hash=sha256:7959b4a74abdc27b312fed1c21e6caf9309ce0b29ea86b591fd2e99ecdf27f73 + +cffi==1.4.2 \ + --hash=sha256:53c1c9ddb30431513eb7f3cdef0a3e06b0f1252188aaa7744af0f5a4cd45dbaf \ + --hash=sha256:a568f49dfca12a8d9f370187257efc58a38109e1eee714d928561d7a018a64f8 \ + --hash=sha256:809c6ca8cfbcaeebfbd432b4576001b40d38ff2463773cb57577d75e1a020bc3 \ + --hash=sha256:86cdca2cd9cba41422230390df17dfeaa9f344a911e3975c8be9da57b35548e9 \ + --hash=sha256:24b13db84aec385ca23c7b8ded83ef8bb4177bc181d14758f9f975be5d020d86 \ + --hash=sha256:969aeffd7c0e097f6be1efd682c156ae226591a0793a94b6c2d5e4293f4c8d4e \ + --hash=sha256:000f358d4b0fa249feaab9c1ce7d5b2fe7e02e7bdf6806c26418505fc685e268 \ + --hash=sha256:a9d86f460bbd8358a2d513ad779e3f3fc878e3b93a00b5002faebf616ffe6b9c \ + --hash=sha256:3127b3ab33eb23ccac071f9a0802748e5cf7c5cbcd02482bb063e35b41dbb0b0 \ + --hash=sha256:e2b2d42236469a40224d39e7b6c60575f388b2f423f354c7ee90a5b7f58c8065 \ + --hash=sha256:8c2dccafee89b1b424b0bec6ad2dd9622c949d2024e929f5da1ed801eac75f1d \ + --hash=sha256:a4de7a4d11aed488bab4fb14f4988587a829bece5a20433f780d6e33b08083cb \ + --hash=sha256:5ca8fe30425265a49274e4b0213a1bc98f4b13449ae5e96f984771e5d83e58c1 \ + --hash=sha256:a4fd38802f59e714eba81a024f62db710b27dbe27a7ea12e911537327aa84d30 \ + --hash=sha256:86cd6912bbc83e9405d4a73cd7f4b4ee8353652d2dbc7c820106ed5b4d1bab3a \ + --hash=sha256:8f1d177d364ea35900415ae24ca3e471be3d5334ed0419294068c49f45913998 +ConfigArgParse==0.10.0 \ + --hash=sha256:3b50a83dd58149dfcee98cb6565265d10b53e9c0a2bca7eeef7fb5f5524890a7 +configobj==5.0.6 \ + --hash=sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902 +cryptography==1.2.3 \ + --hash=sha256:031938f73a5c5eb3e809e18ff7caeb6865351871417be6050cb8c86a9a202b9a \ + --hash=sha256:a179a38d50f8d68b491d7a313db78f8cabe290842cecddddc7b34d408e59db0a \ + --hash=sha256:906c88b2aadcf99cfabb24098263d1bf65ab0c8688acde10dae1f09d865920f1 \ + --hash=sha256:6e706c5c6088770b1d1b634e959e21963e315b0255f5f4777125ad3d54082977 \ + --hash=sha256:f5ebf8e31c48f8707921dca0e994de77813a9c9b9bf03c119c5ddf97bdcffe73 \ + --hash=sha256:c7b89e42288cc7fbee3812e99ef5c744f22452e11d6822f6807afc6d6b3be83e \ + --hash=sha256:8408d29865947109d8b68f1837a7cde1aa4dc86e0f79ca3ba58c0c44e443d6a5 \ + --hash=sha256:c7e76cf3c3d925dd31fa238cfb806cffba718c0f08707d77a538768477969956 \ + --hash=sha256:7d8de35380f31702758b7753bb5c40723832c73006dedb2f9099bf61a37f7287 \ + --hash=sha256:5edbee71fae5469ee83fe0a37866b9398c8ce3a46325c24fcedfbf097bb48a19 \ + --hash=sha256:594edafe4801c13bdc1cc305e7704a90c19617e95936f6ab457ee4ffe000ba50 \ + --hash=sha256:b7fdb16a0a7f481be42da744bfe1ea2163025de21f90f2c688a316f3c354da9c \ + --hash=sha256:207b8bf0fe0907336df38b733b487521cf9e138189aba9234ad54fe545dd0db8 \ + --hash=sha256:509a2f05386270cf783993c90d49ffefb3dd62aee45bf1ea8ce3d2cde7271c21 \ + --hash=sha256:ac69b65dd1af0179ede40c9f15788c88f73e628ea6c0519de3838e279bb388c6 \ + --hash=sha256:8df6fad6c6ae12fd7004ea29357f0a2b4d3774eaeca7656530d08d2d90cd41aa \ + --hash=sha256:0b8b96dd81cc1533a04f30382c0fe21c1972e189f794d0c4261a18cec08fd9b5 \ + --hash=sha256:cae8fca1883f23c50ea78d89de6fe4fefdb4cea83177760f47177559414ded93 \ + --hash=sha256:1a471ca576a9cdce1b1cd9f3a22b1d09ee44d46862037557de17919c0db44425 \ + --hash=sha256:8ec4e8e3d453b3a1b63b5f57737a434dcf1ee4a2f26f6ff7c5a37c3f679104d2 \ + --hash=sha256:8eb11c77dd8e73f48df6b2f7a7e16173fe0fe8fdfe266232832e88477e08454e +enum34==1.1.2 \ + --hash=sha256:2475d7fcddf5951e92ff546972758802de5260bf409319a9f1934e6bbc8b1dc7 \ + --hash=sha256:35907defb0f992b75ab7788f65fedc1cf20ffa22688e0e6f6f12afc06b3ea501 +funcsigs==0.4 \ + --hash=sha256:ff5ad9e2f8d9e5d1e8bbfbcf47722ab527cf0d51caeeed9da6d0f40799383fde \ + --hash=sha256:d83ce6df0b0ea6618700fe1db353526391a8a3ada1b7aba52fed7a61da772033 +idna==2.0 \ + --hash=sha256:9b2fc50bd3c4ba306b9651b69411ef22026d4d8335b93afc2214cef1246ce707 \ + --hash=sha256:16199aad938b290f5be1057c0e1efc6546229391c23cea61ca940c115f7d3d3b +ipaddress==1.0.16 \ + --hash=sha256:935712800ce4760701d89ad677666cd52691fd2f6f0b340c8b4239a3c17988a5 \ + --hash=sha256:5a3182b322a706525c46282ca6f064d27a02cffbd449f9f47416f1dc96aa71b0 +linecache2==1.0.0 \ + --hash=sha256:e78be9c0a0dfcbac712fe04fbf92b96cddae80b1b842f24248214c8496f006ef \ + --hash=sha256:4b26ff4e7110db76eeb6f5a7b64a82623839d595c2038eeda662f2a2db78e97c +ndg-httpsclient==0.4.0 \ + --hash=sha256:e8c155fdebd9c4bcb0810b4ed01ae1987554b1ee034dd7532d7b8fdae38a6274 +ordereddict==1.1 \ + --hash=sha256:1c35b4ac206cef2d24816c89f89cf289dd3d38cf7c449bb3fab7bf6d43f01b1f +parsedatetime==2.1 \ + --hash=sha256:ce9d422165cf6e963905cd5f74f274ebf7cc98c941916169178ef93f0e557838 \ + --hash=sha256:17c578775520c99131634e09cfca5a05ea9e1bd2a05cd06967ebece10df7af2d +pbr==1.8.1 \ + --hash=sha256:46c8db75ae75a056bd1cc07fa21734fe2e603d11a07833ecc1eeb74c35c72e0c \ + --hash=sha256:e2127626a91e6c885db89668976db31020f0af2da728924b56480fc7ccf09649 +psutil==3.3.0 \ + --hash=sha256:584f0b29fcc5d523b433cb8918b2fc74d67e30ee0b44a95baf031528f424619f \ + --hash=sha256:28ca0b6e9d99aa8dc286e8747a4471362b69812a25291de29b6a8d70a1545a0d \ + --hash=sha256:167ad5fff52a672c4ddc1c1a0b25146d6813ebb08a9aab0a3ac45f8a5b669c3b \ + --hash=sha256:e6dea6173a988727bb223d3497349ad5cdef5c0b282eff2d83e5f9065c53f85f \ + --hash=sha256:2af5e0a4aad66049955d0734aa4e3dc8caa17a9eaf8b4c1a27a5f1ee6e40f6fc \ + --hash=sha256:d9884dc0dc2e55e2448e495778dc9899c1c8bf37aeb2f434c1bea74af93c2683 \ + --hash=sha256:e27c2fe6dfcc8738be3d2c5a022f785eb72971057e1a9e1e34fba73bce8a71a6 \ + --hash=sha256:65afd6fecc8f3aed09ee4be63583bc8eb472f06ceaa4fe24c4d1d5a1a3c0e13f \ + --hash=sha256:ba1c558fbfcdf94515c2394b1155c1dc56e2bc2a9c17d30349827c9ed8a67e46 \ + --hash=sha256:ba95ea0022dcb64d36f0c1335c0605fae35bdf3e0fea8d92f5d0f6456a35e55b \ + --hash=sha256:421b6591d16b509aaa8d8c15821d66bb94cb4a8dc4385cad5c51b85d4a096d85 \ + --hash=sha256:326b305cbdb6f94dafbfe2c26b11da88b0ab07b8a07f8188ab9d75ff0c6e841a \ + --hash=sha256:9aede5b2b6fe46b3748ea8e5214443890d1634027bef3d33b7dad16556830278 \ + --hash=sha256:73bed1db894d1aa9c3c7e611d302cdeab7ae8a0dc0eeaf76727878db1ac5cd87 \ + --hash=sha256:935b5dd6d558af512f42501a7c08f41d7aff139af1bb3959daa3abb859234d6c \ + --hash=sha256:4ca0111cf157dcc0f2f69a323c5b5478718d68d45fc9435d84be0ec0f186215b \ + --hash=sha256:b6f13c95398a3fcf0226c4dcfa448560ba5865259cd96ec2810658651e932189 \ + --hash=sha256:ee6be30d1635bbdea4c4325d507dc8a0dbbde7e1c198bd62ddb9f43198b9e214 \ + --hash=sha256:dfa786858c268d7fbbe1b6175e001ec02738d7cfae0a7ce77bf9b651af676729 \ + --hash=sha256:aa77f9de72af9c16cc288cd4a24cf58824388f57d7a81e400c4616457629870e \ + --hash=sha256:f500093357d04da8140d87932cac2e54ef592a54ca8a743abb2850f60c2c22eb +pyasn1==0.1.9 \ + --hash=sha256:61f9d99e3cef65feb1bfe3a2eef7a93eb93819d345bf54bcd42f4e63d5204dae \ + --hash=sha256:1802a6dd32045e472a419db1441aecab469d33e0d2749e192abdec52101724af \ + --hash=sha256:35025cd9422c96504912f04e2f15fe79390a8597b430c2ca5d0534cf9309ffa0 \ + --hash=sha256:2f96ed5a0c329ca16230b326ca12b7461ec8f65e0be3e4f997516f36bf82a345 \ + --hash=sha256:28fee44217991cfad9e6a0b9f7e3f26041e21ebc96629e94e585ccd05d49fa65 \ + --hash=sha256:326e7a854a17fab07691204747695f8f692d674588a355c441fb14f660bf4e68 \ + --hash=sha256:cda5a90485709ca6795c86056c3e5fe7266028b05e53f1d527fdf93a6365a6b8 \ + --hash=sha256:0cb2a14742b543fdd68f931a14ce3829186ed2b1b2267a06787388c96b2dd9be \ + --hash=sha256:5191ff6b9126d2c039dd87f8ff025bed274baf07fa78afa46f556b1ad7265d6e \ + --hash=sha256:8323e03637b2d072cc7041300bac6ec448c3c28950ab40376036788e9a1af629 \ + --hash=sha256:853cacd96d1f701ddd67aa03ecc05f51890135b7262e922710112f12a2ed2a7f +pyOpenSSL==0.15.1 \ + --hash=sha256:88e45e6bb25dfed272a1ef2e728461d44b634c2cd689e989b6e56a349c5a3ae5 \ + --hash=sha256:f0a26070d6db0881de8bcc7846934b7c3c930d8f9c79d45883ee48984bc0d672 +pyRFC3339==1.0 \ + --hash=sha256:eea31835c56e2096af4363a5745a784878a61d043e247d3a6d6a0a32a9741f56 \ + --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 +python-augeas==0.5.0 \ + --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 +python2-pythondialog==3.3.0 \ + --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ + --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa +pytz==2015.7 \ + --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ + --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ + --hash=sha256:ead4aefa7007249e05e51b01095719d5a8dd95760089f5730aac5698b1932918 \ + --hash=sha256:3cca0df08bd0ed98432390494ce3ded003f5e661aa460be7a734bffe35983605 \ + --hash=sha256:3ede470d3d17ba3c07638dfa0d10452bc1b6e5ad326127a65ba77e6aaeb11bec \ + --hash=sha256:68c47964f7186eec306b13629627722b9079cd4447ed9e5ecaecd4eac84ca734 \ + --hash=sha256:dd5d3991950aae40a6c81de1578942e73d629808cefc51d12cd157980e6cfc18 \ + --hash=sha256:a77c52062c07eb7c7b30545dbc73e32995b7e117eea750317b5cb5c7a4618f14 \ + --hash=sha256:81af9aec4bc960a9a0127c488f18772dae4634689233f06f65443e7b11ebeb51 \ + --hash=sha256:e079b1dadc5c06246cc1bb6fe1b23a50b1d1173f2edd5104efd40bb73a28f406 \ + --hash=sha256:fbd26746772c24cb93c8b97cbdad5cb9e46c86bbdb1b9d8a743ee00e2fb1fc5d \ + --hash=sha256:99266ef30a37e43932deec2b7ca73e83c8dbc3b9ff703ec73eca6b1dae6befea \ + --hash=sha256:8b6ce1c993909783bc96e0b4f34ea223bff7a4df2c90bdb9c4e0f1ac928689e3 +requests==2.9.1 \ + --hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \ + --hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f +six==1.10.0 \ + --hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \ + --hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a +traceback2==1.4.0 \ + --hash=sha256:8253cebec4b19094d67cc5ed5af99bf1dba1285292226e98a31929f87a5d6b23 \ + --hash=sha256:05acc67a09980c2ecfedd3423f7ae0104839eccb55fc645773e1caa0951c3030 +unittest2==1.1.0 \ + --hash=sha256:13f77d0875db6d9b435e1d4f41e74ad4cc2eb6e1d5c824996092b3430f088bb8 \ + --hash=sha256:22882a0e418c284e1f718a822b3b022944d53d2d908e1690b319a9d3eb2c0579 +zope.component==4.2.2 \ + --hash=sha256:282c112b55dd8e3c869a3571f86767c150ab1284a9ace2bdec226c592acaf81a +zope.event==4.1.0 \ + --hash=sha256:dc7a59a2fd91730d3793131a5d261b29e93ec4e2a97f1bc487ce8defee2fe786 +zope.interface==4.1.3 \ + --hash=sha256:f07b631f7a601cd8cbd3332d54f43142c7088a83299f859356f08d1d4d4259b3 \ + --hash=sha256:de5cca083b9439d8002fb76bbe6b4998c5a5a721fab25b84298967f002df4c94 \ + --hash=sha256:6788416f7ea7f5b8a97be94825377aa25e8bdc73463e07baaf9858b29e737077 \ + --hash=sha256:6f3230f7254518201e5a3708cbb2de98c848304f06e3ded8bfb39e5825cba2e1 \ + --hash=sha256:5fa575a5240f04200c3088427d0d4b7b737f6e9018818a51d8d0f927a6a2517a \ + --hash=sha256:522194ad6a545735edd75c8a83f48d65d1af064e432a7d320d64f56bafc12e99 \ + --hash=sha256:e8c7b2d40943f71c99148c97f66caa7f5134147f57423f8db5b4825099ce9a09 \ + --hash=sha256:279024f0208601c3caa907c53876e37ad88625f7eaf1cb3842dbe360b2287017 \ + --hash=sha256:2e221a9eec7ccc58889a278ea13dcfed5ef939d80b07819a9a8b3cb1c681484f \ + --hash=sha256:69118965410ec86d44dc6b9017ee3ddbd582e0c0abeef62b3a19dbf6c8ad132b \ + --hash=sha256:d04df8686ec864d0cade8cf199f7f83aecd416109a20834d568f8310ded12dea \ + --hash=sha256:e75a947e15ee97e7e71e02ea302feb2fc62d3a2bb4668bf9dfbed43a506ac7e7 \ + --hash=sha256:4e45d22fb883222a5ab9f282a116fec5ee2e8d1a568ccff6a2d75bbd0eb6bcfc \ + --hash=sha256:bce9339bb3c7a55e0803b63d21c5839e8e479bc85c4adf42ae415b72f94facb2 \ + --hash=sha256:928138365245a0e8869a5999fbcc2a45475a0a6ed52a494d60dbdc540335fedd \ + --hash=sha256:0d841ba1bb840eea0e6489dc5ecafa6125554971f53b5acb87764441e61bceba \ + --hash=sha256:b09c8c1d47b3531c400e0195697f1414a63221de6ef478598a4f1460f7d9a392 +mock==1.0.1 \ + --hash=sha256:b839dd2d9c117c701430c149956918a423a9863b48b09c90e30a6013e7d2f44f \ + --hash=sha256:8f83080daa249d036cbccfb8ae5cc6ff007b88d6d937521371afabe7b19badbc + +# THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. + +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 + +UNLIKELY_EOF + # ------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/pipstrap.py" +#!/usr/bin/env python +"""A small script that can act as a trust root for installing pip 8 + +Embed this in your project, and your VCS checkout is all you have to trust. In +a post-peep era, this lets you claw your way to a hash-checking version of pip, +with which you can install the rest of your dependencies safely. All it assumes +is Python 2.6 or better and *some* version of pip already installed. If +anything goes wrong, it will exit with a non-zero status code. + +""" +# This is here so embedded copies are MIT-compliant: +# Copyright (c) 2016 Erik Rose +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +from __future__ import print_function +from hashlib import sha256 +from os.path import join +from pipes import quote +from shutil import rmtree +try: + from subprocess import check_output +except ImportError: + from subprocess import CalledProcessError, PIPE, Popen + + def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be ' + 'overridden.') + process = Popen(stdout=PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd) + return output +from sys import exit, version_info +from tempfile import mkdtemp +try: + from urllib2 import build_opener, HTTPHandler, HTTPSHandler +except ImportError: + from urllib.request import build_opener, HTTPHandler, HTTPSHandler +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse # 3.4 + + +__version__ = 1, 1, 1 + + +# wheel has a conditional dependency on argparse: +maybe_argparse = ( + [('https://pypi.python.org/packages/source/a/argparse/' + 'argparse-1.4.0.tar.gz', + '62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4')] + if version_info < (2, 7, 0) else []) + + +PACKAGES = maybe_argparse + [ + # Pip has no dependencies, as it vendors everything: + ('https://pypi.python.org/packages/source/p/pip/pip-8.0.3.tar.gz', + '30f98b66f3fe1069c529a491597d34a1c224a68640c82caf2ade5f88aa1405e8'), + # This version of setuptools has only optional dependencies: + ('https://pypi.python.org/packages/source/s/setuptools/' + 'setuptools-20.2.2.tar.gz', + '24fcfc15364a9fe09a220f37d2dcedc849795e3de3e4b393ee988e66a9cbd85a'), + ('https://pypi.python.org/packages/source/w/wheel/wheel-0.29.0.tar.gz', + '1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648') +] + + +class HashError(Exception): + def __str__(self): + url, path, actual, expected = self.args + return ('{url} did not match the expected hash {expected}. Instead, ' + 'it was {actual}. The file (left at {path}) may have been ' + 'tampered with.'.format(**locals())) + + +def hashed_download(url, temp, digest): + """Download ``url`` to ``temp``, make sure it has the SHA-256 ``digest``, + and return its path.""" + # Based on pip 1.4.1's URLOpener but with cert verification removed. Python + # >=2.7.9 verifies HTTPS certs itself, and, in any case, the cert + # authenticity has only privacy (not arbitrary code execution) + # implications, since we're checking hashes. + def opener(): + opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in opener.handlers: + if isinstance(handler, HTTPHandler): + opener.handlers.remove(handler) + return opener + + def read_chunks(response, chunk_size): + while True: + chunk = response.read(chunk_size) + if not chunk: + break + yield chunk + + response = opener().open(url) + path = join(temp, urlparse(url).path.split('/')[-1]) + actual_hash = sha256() + with open(path, 'wb') as file: + for chunk in read_chunks(response, 4096): + file.write(chunk) + actual_hash.update(chunk) + + actual_digest = actual_hash.hexdigest() + if actual_digest != digest: + raise HashError(url, path, actual_digest, digest) + return path + + +def main(): + temp = mkdtemp(prefix='pipstrap-') + try: + downloads = [hashed_download(url, temp, digest) + for url, digest in PACKAGES] + check_output('pip install --no-index --no-deps -U ' + + ' '.join(quote(d) for d in downloads), + shell=True) + except HashError as exc: + print(exc) + except Exception: + rmtree(temp) + raise + else: + rmtree(temp) + return 0 + return 1 + + +if __name__ == '__main__': + exit(main()) + +UNLIKELY_EOF + # ------------------------------------------------------------------------- + # Set PATH so pipstrap upgrades the right (v)env: + PATH="$VENV_BIN:$PATH" "$VENV_BIN/python" "$TEMP_DIR/pipstrap.py" + set +e + PIP_OUT=`"$VENV_BIN/pip" install --no-cache-dir --require-hashes -r "$TEMP_DIR/letsencrypt-auto-requirements.txt" 2>&1` + PIP_STATUS=$? + set -e + rm -rf "$TEMP_DIR" + if [ "$PIP_STATUS" != 0 ]; then + # Report error. (Otherwise, be quiet.) + echo "Had a problem while installing Python packages:" + echo "$PIP_OUT" + rm -rf "$VENV_PATH" + exit 1 + fi + echo "Installation succeeded." + fi + echo "Requesting root privileges to run certbot..." + if [ -z "$SUDO_ENV" ] ; then + # SUDO is su wrapper / noop + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" + else + # sudo + echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + fi + +else + # Phase 1: Upgrade certbot-auto if neceesary, then self-invoke. + # + # Each phase checks the version of only the thing it is responsible for + # upgrading. Phase 1 checks the version of the latest release of + # certbot-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. + + if [ ! -f "$VENV_BIN/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi + # If it looks like we've never bootstrapped before, bootstrap: + Bootstrap + fi + if [ "$OS_PACKAGES_ONLY" = 1 ]; then + echo "OS packages installed." + exit 0 + fi + + if [ "$NO_SELF_UPGRADE" != 1 ]; then + echo "Checking for new version..." + TEMP_DIR=$(TempDir) + # --------------------------------------------------------------------------- + cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" +"""Do downloading and JSON parsing without additional dependencies. :: + + # Print latest released version of LE to stdout: + python fetch.py --latest-version + + # Download letsencrypt-auto script from git tag v1.2.3 into the folder I'm + # in, and make sure its signature verifies: + python fetch.py --le-auto-script v1.2.3 + +On failure, return non-zero. + +""" +from distutils.version import LooseVersion +from json import loads +from os import devnull, environ +from os.path import dirname, join +import re +from subprocess import check_call, CalledProcessError +from sys import argv, exit +from urllib2 import build_opener, HTTPHandler, HTTPSHandler, HTTPError + +PUBLIC_KEY = environ.get('LE_AUTO_PUBLIC_KEY', """-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6MR8W/galdxnpGqBsYbq +OzQb2eyW15YFjDDEMI0ZOzt8f504obNs920lDnpPD2/KqgsfjOgw2K7xWDJIj/18 +xUvWPk3LDkrnokNiRkA3KOx3W6fHycKL+zID7zy+xZYBuh2fLyQtWV1VGQ45iNRp +9+Zo7rH86cdfgkdnWTlNSHyTLW9NbXvyv/E12bppPcEvgCTAQXgnDVJ0/sqmeiij +n9tTFh03aM+R2V/21h8aTraAS24qiPCz6gkmYGC8yr6mglcnNoYbsLNYZ69zF1XH +cXPduCPdPdfLlzVlKK1/U7hkA28eG3BIAMh6uJYBRJTpiGgaGdPd7YekUB8S6cy+ +CQIDAQAB +-----END PUBLIC KEY----- +""") + +class ExpectedError(Exception): + """A novice-readable exception that also carries the original exception for + debugging""" + + +class HttpsGetter(object): + def __init__(self): + """Build an HTTPS opener.""" + # Based on pip 1.4.1's URLOpener + # This verifies certs on only Python >=2.7.9. + self._opener = build_opener(HTTPSHandler()) + # Strip out HTTPHandler to prevent MITM spoof: + for handler in self._opener.handlers: + if isinstance(handler, HTTPHandler): + self._opener.handlers.remove(handler) + + def get(self, url): + """Return the document contents pointed to by an HTTPS URL. + + If something goes wrong (404, timeout, etc.), raise ExpectedError. + + """ + try: + return self._opener.open(url).read() + except (HTTPError, IOError) as exc: + raise ExpectedError("Couldn't download %s." % url, exc) + + +def write(contents, dir, filename): + """Write something to a file in a certain directory.""" + with open(join(dir, filename), 'w') as file: + file.write(contents) + + +def latest_stable_version(get): + """Return the latest stable release of letsencrypt.""" + metadata = loads(get( + environ.get('LE_AUTO_JSON_URL', + 'https://pypi.python.org/pypi/letsencrypt/json'))) + # metadata['info']['version'] actually returns the latest of any kind of + # release release, contrary to https://wiki.python.org/moin/PyPIJSON. + # The regex is a sufficient regex for picking out prereleases for most + # packages, LE included. + return str(max(LooseVersion(r) for r + in metadata['releases'].iterkeys() + if re.match('^[0-9.]+$', r))) + + +def verified_new_le_auto(get, tag, temp_dir): + """Return the path to a verified, up-to-date letsencrypt-auto script. + + If the download's signature does not verify or something else goes wrong + with the verification process, raise ExpectedError. + + """ + le_auto_dir = environ.get( + 'LE_AUTO_DIR_TEMPLATE', + 'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/' + 'letsencrypt-auto-source/') % tag + write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto') + write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig') + write(PUBLIC_KEY, temp_dir, 'public_key.pem') + try: + with open(devnull, 'w') as dev_null: + check_call(['openssl', 'dgst', '-sha256', '-verify', + join(temp_dir, 'public_key.pem'), + '-signature', + join(temp_dir, 'letsencrypt-auto.sig'), + join(temp_dir, 'letsencrypt-auto')], + stdout=dev_null, + stderr=dev_null) + except CalledProcessError as exc: + raise ExpectedError("Couldn't verify signature of downloaded " + "certbot-auto.", exc) + + +def main(): + get = HttpsGetter().get + flag = argv[1] + try: + if flag == '--latest-version': + print latest_stable_version(get) + elif flag == '--le-auto-script': + tag = argv[2] + verified_new_le_auto(get, tag, dirname(argv[0])) + except ExpectedError as exc: + print exc.args[0], exc.args[1] + return 1 + else: + return 0 + + +if __name__ == '__main__': + exit(main()) + +UNLIKELY_EOF + # --------------------------------------------------------------------------- + DeterminePythonVersion + if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then + echo "WARNING: unable to check for updates." + elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + + # Now we drop into Python so we don't have to install even more + # dependencies (curl, etc.), for better flow control, and for the option of + # future Windows compatibility. + "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" + + # Install new copy of certbot-auto. + # TODO: Deal with quotes in pathnames. + echo "Replacing certbot-auto..." + # Clone permissions with cp. chmod and chown don't have a --reference + # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: + $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" + $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" + # Using mv rather than cp leaves the old file descriptor pointing to the + # original copy so the shell can continue to read it unmolested. mv across + # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the + # cp is unlikely to fail (esp. under sudo) if the rm doesn't. + $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" + # TODO: Clean up temp dir safely, even if it has quotes in its path. + rm -rf "$TEMP_DIR" + fi # A newer version is available. + fi # Self-upgrading is allowed. + + "$0" --le-auto-phase2 "$@" +fi diff --git a/certbot-compatibility-test/docs/conf.py b/certbot-compatibility-test/docs/conf.py index 1ef69ab2d..f89f4b368 100644 --- a/certbot-compatibility-test/docs/conf.py +++ b/certbot-compatibility-test/docs/conf.py @@ -311,7 +311,7 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), 'certbot-apache': ( 'https://letsencrypt-apache.readthedocs.org/en/latest/', None), 'certbot-nginx': ( diff --git a/certbot-compatibility-test/setup.py b/certbot-compatibility-test/setup.py index c62a10f89..8f9452c5a 100644 --- a/certbot-compatibility-test/setup.py +++ b/certbot-compatibility-test/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.7.0.dev0' install_requires = [ 'certbot=={0}'.format(version), diff --git a/certbot-nginx/certbot_nginx/nginxparser.py b/certbot-nginx/certbot_nginx/nginxparser.py index 8ab7adb0a..1577c7b1e 100644 --- a/certbot-nginx/certbot_nginx/nginxparser.py +++ b/certbot-nginx/certbot_nginx/nginxparser.py @@ -30,10 +30,11 @@ class RawNginxParser(object): assignment = (key + Optional(space + value, default=None) + semicolon) location_statement = Optional(space + modifier) + Optional(space + location) if_statement = Literal("if") + space + Regex(r"\(.+\)") + space + map_statement = Literal("map") + space + Regex(r"\S+") + space + Regex(r"\$\S+") + space block = Forward() block << Group( - (Group(key + location_statement) ^ Group(if_statement)) + + (Group(key + location_statement) ^ Group(if_statement) ^ Group(map_statement)) + left_bracket + Group(ZeroOrMore(Group(comment | assignment) | block)) + right_bracket) diff --git a/certbot-nginx/docs/conf.py b/certbot-nginx/docs/conf.py index fa00e6503..167abb4fb 100644 --- a/certbot-nginx/docs/conf.py +++ b/certbot-nginx/docs/conf.py @@ -307,5 +307,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), } diff --git a/certbot-nginx/setup.py b/certbot-nginx/setup.py index 0e5c27a0a..a74b93093 100644 --- a/certbot-nginx/setup.py +++ b/certbot-nginx/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.7.0.dev0' # Please update tox.ini when modifying dependency version requirements install_requires = [ diff --git a/certbot/__init__.py b/certbot/__init__.py index a48d62548..85f370e7a 100644 --- a/certbot/__init__.py +++ b/certbot/__init__.py @@ -1,4 +1,4 @@ """Certbot client.""" # version number like 1.2.3a0, must have at least 2 parts, like 1.2 -__version__ = '0.6.0.dev0' +__version__ = '0.7.0.dev0' diff --git a/certbot/cli.py b/certbot/cli.py index 430384a5a..af4cd3013 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -699,6 +699,9 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): helpful.add( "security", "--rsa-key-size", type=int, metavar="N", default=flag_default("rsa_key_size"), help=config_help("rsa_key_size")) + helpful.add( + "security", "--must-staple", action="store_true", + help=config_help("must_staple"), dest="must_staple", default=False) helpful.add( "security", "--redirect", action="store_true", help="Automatically redirect all HTTP traffic to HTTPS for the newly " @@ -723,9 +726,20 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): " https:// for every http:// resource.", dest="uir", default=None) helpful.add( "security", "--no-uir", action="store_false", - help=" Do not automatically set the \"Content-Security-Policy:" + help="Do not automatically set the \"Content-Security-Policy:" " upgrade-insecure-requests\" header to every HTTP response.", dest="uir", default=None) + helpful.add( + "security", "--staple-ocsp", action="store_true", + help="Enables OCSP Stapling. A valid OCSP response is stapled to" + " the certificate that the server offers during TLS.", + dest="staple", default=None) + helpful.add( + "security", "--no-staple-ocsp", action="store_false", + help="Do not automatically enable OCSP Stapling.", + dest="staple", default=None) + + helpful.add( "security", "--strict-permissions", action="store_true", help="Require that all configuration files are owned by the current " @@ -740,7 +754,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): " certificate lineage. You can try it with `--dry-run` first. For" " more fine-grained control, you can renew individual lineages with" " the `certonly` subcommand. Hooks are available to run commands " - " before and after renewal; see XXX for more information on these.") + " before and after renewal; see" + " https://certbot.eff.org/docs/using.html#renewal for more information on these.") helpful.add( "renew", "--pre-hook", @@ -752,7 +767,8 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): "renew", "--post-hook", help="Command to be run in a shell after attempting to obtain/renew " " certificates. Can be used to deploy renewed certificates, or to restart" - " any servers that were stopped by --pre-hook.") + " any servers that were stopped by --pre-hook. This is only run if" + " an attempt was made to obtain/renew a certificate.") helpful.add( "renew", "--renew-hook", help="Command to be run in a shell once for each successfully renewed certificate." diff --git a/certbot/client.py b/certbot/client.py index 6f41a3a0b..0159d3946 100644 --- a/certbot/client.py +++ b/certbot/client.py @@ -396,7 +396,8 @@ class Client(object): supported = self.installer.supported_enhancements() redirect = config.redirect if "redirect" in supported else False hsts = config.hsts if "ensure-http-header" in supported else False - uir = config.uir if "ensure-http-header" in supported else False + uir = config.uir if "ensure-http-header" in supported else False + staple = config.staple if "staple-ocsp" in supported else False if redirect is None: redirect = enhancements.ask("redirect") @@ -410,9 +411,11 @@ class Client(object): if uir: self.apply_enhancement(domains, "ensure-http-header", "Upgrade-Insecure-Requests") + if staple: + self.apply_enhancement(domains, "staple-ocsp") msg = ("We were unable to restart web server") - if redirect or hsts or uir: + if redirect or hsts or uir or staple: with error_handler.ErrorHandler(self._rollback_and_restart, msg): self.installer.restart() diff --git a/certbot/crypto_util.py b/certbot/crypto_util.py index 07e7f9fd2..ee0382cd5 100644 --- a/certbot/crypto_util.py +++ b/certbot/crypto_util.py @@ -76,9 +76,11 @@ def init_save_csr(privkey, names, path, csrname="csr-certbot.pem"): :rtype: :class:`certbot.le_util.CSR` """ - csr_pem, csr_der = make_csr(privkey.pem, names) - config = zope.component.getUtility(interfaces.IConfig) + + csr_pem, csr_der = make_csr(privkey.pem, names, + must_staple=config.must_staple) + # Save CSR le_util.make_or_verify_dir(path, 0o755, os.geteuid(), config.strict_permissions) @@ -93,7 +95,7 @@ def init_save_csr(privkey, names, path, csrname="csr-certbot.pem"): # Lower level functions -def make_csr(key_str, domains): +def make_csr(key_str, domains, must_staple=False): """Generate a CSR. :param str key_str: PEM-encoded RSA key. @@ -112,13 +114,19 @@ def make_csr(key_str, domains): req.get_subject().CN = domains[0] # TODO: what to put into req.get_subject()? # TODO: put SAN if len(domains) > 1 - req.add_extensions([ + extensions = [ OpenSSL.crypto.X509Extension( "subjectAltName", critical=False, value=", ".join("DNS:%s" % d for d in domains) - ), - ]) + ) + ] + if must_staple: + extensions.append(OpenSSL.crypto.X509Extension( + "1.3.6.1.5.5.7.1.24", + critical=False, + value="DER:30:03:02:01:05")) + req.add_extensions(extensions) req.set_version(2) req.set_pubkey(pkey) req.sign(pkey, "sha256") diff --git a/certbot/hooks.py b/certbot/hooks.py index 138e2addc..1a3e4a98e 100644 --- a/certbot/hooks.py +++ b/certbot/hooks.py @@ -27,7 +27,7 @@ def _validate_hook(shell_cmd, hook_name): :raises .errors.HookCommandNotFound: if the command is not found """ if shell_cmd: - cmd = shell_cmd.partition(" ")[0] + cmd = shell_cmd.split(None, 1)[0] if not _prog(cmd): path = os.environ["PATH"] msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format( @@ -39,7 +39,7 @@ def pre_hook(config): if config.pre_hook and not pre_hook.already: logger.info("Running pre-hook command: %s", config.pre_hook) _run_hook(config.pre_hook) - pre_hook.already = True + pre_hook.already = True pre_hook.already = False @@ -50,6 +50,11 @@ def post_hook(config, final=False): we're called with final=True before actually doing anything. """ if config.post_hook: + if not pre_hook.already: + logger.info("No renewals attempted, so not running post-hook") + if config.verb != "renew": + logger.warn("Sanity failure in renewal hooks") + return if final or config.verb != "renew": logger.info("Running post-hook command: %s", config.post_hook) _run_hook(config.post_hook) diff --git a/certbot/interfaces.py b/certbot/interfaces.py index d65f5cf01..8e8666e70 100644 --- a/certbot/interfaces.py +++ b/certbot/interfaces.py @@ -200,6 +200,10 @@ class IConfig(zope.interface.Interface): email = zope.interface.Attribute( "Email used for registration and recovery contact.") rsa_key_size = zope.interface.Attribute("Size of the RSA key.") + must_staple = zope.interface.Attribute( + "Whether to request the OCSP Must Staple certificate extension. " + "Additional setup may be required after issuance. This does not " + "currently autoconfigure web servers for OCSP stapling. ") config_dir = zope.interface.Attribute("Configuration directory.") work_dir = zope.interface.Attribute("Working directory.") diff --git a/certbot/main.py b/certbot/main.py index 398da71a5..4ef2e6ac8 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -94,10 +94,10 @@ def _auth_from_domains(le_client, config, domains, lineage=None): if lineage is False: raise errors.Error("Certificate could not be obtained") finally: - hooks.post_hook(config) + hooks.post_hook(config, final=False) if not config.dry_run and not config.verb == "renew": - _report_new_cert(lineage.cert, lineage.fullchain) + _report_new_cert(config, lineage.cert, lineage.fullchain) return lineage, action @@ -267,7 +267,7 @@ def _find_domains(config, installer): return domains -def _report_new_cert(cert_path, fullchain_path): +def _report_new_cert(config, cert_path, fullchain_path): """Reports the creation of a new certificate to the user. :param str cert_path: path to cert @@ -285,12 +285,15 @@ def _report_new_cert(cert_path, fullchain_path): # Unless we're in .csr mode and there really isn't one and_chain = "has " path = cert_path + + verbswitch = ' with the "certonly" option' if config.verb == "run" else "" # XXX Perhaps one day we could detect the presence of known old webservers # and say something more informative here. - msg = ("Congratulations! Your certificate {0} been saved at {1}." - " Your cert will expire on {2}. To obtain a new version of the " - "certificate in the future, simply run Certbot again." - .format(and_chain, path, expiry)) + msg = ('Congratulations! Your certificate {0} been saved at {1}.' + ' Your cert will expire on {2}. To obtain a new or tweaked version of this ' + 'certificate in the future, simply run {3} again{4}. ' + 'To non-interactively renew *all* of your ceriticates, run "{3} renew"' + .format(and_chain, path, expiry, cli.cli_command, verbswitch)) reporter_util.add_message(msg, reporter_util.MEDIUM_PRIORITY) @@ -485,7 +488,7 @@ def _csr_obtain_cert(config, le_client): else: cert_path, _, cert_fullchain = le_client.save_certificate( certr, chain, config.cert_path, config.chain_path, config.fullchain_path) - _report_new_cert(cert_path, cert_fullchain) + _report_new_cert(config, cert_path, cert_fullchain) def obtain_cert(config, plugins, lineage=None): diff --git a/certbot/renewal.py b/certbot/renewal.py index 3682c50d5..b5b982972 100644 --- a/certbot/renewal.py +++ b/certbot/renewal.py @@ -301,7 +301,10 @@ def _renew_describe_results(config, renew_successes, renew_failures, def renew_all_lineages(config): """Examine each lineage; renew if due and report results""" - if config.domains != []: + # This is trivially False if config.domains is empty + if any(domain not in config.webroot_map for domain in config.domains): + # If more plugins start using cli.add_domains, + # we may want to only log a warning here raise errors.Error("Currently, the renew verb is only capable of " "renewing all installed certificates that are due " "to be renewed; individual domains cannot be " diff --git a/certbot/tests/cli_test.py b/certbot/tests/cli_test.py index 31056cafe..d7965a24e 100644 --- a/certbot/tests/cli_test.py +++ b/certbot/tests/cli_test.py @@ -712,6 +712,12 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._test_renew_common(renewalparams=renewalparams, assert_oc_called=True) + def test_renew_with_webroot_map(self): + renewalparams = {'authenticator': 'webroot'} + self._test_renew_common( + renewalparams=renewalparams, assert_oc_called=True, + args=['renew', '--webroot-map', '{"example.com": "/tmp"}']) + def test_renew_reconstitute_error(self): # pylint: disable=protected-access with mock.patch('certbot.main.renewal._reconstitute') as mock_reconstitute: diff --git a/certbot/tests/crypto_util_test.py b/certbot/tests/crypto_util_test.py index 52e595577..ff8d8142e 100644 --- a/certbot/tests/crypto_util_test.py +++ b/certbot/tests/crypto_util_test.py @@ -95,6 +95,25 @@ class MakeCSRTest(unittest.TestCase): ['example.com', 'www.example.com'], get_sans_from_csr( csr_der, OpenSSL.crypto.FILETYPE_ASN1)) + def test_must_staple(self): + # TODO: Fails for RSA256_KEY + csr_pem, _ = self._call( + RSA512_KEY, ['example.com', 'www.example.com'], must_staple=True) + csr = OpenSSL.crypto.load_certificate_request( + OpenSSL.crypto.FILETYPE_PEM, csr_pem) + + # In pyopenssl 0.13 (used with TOXENV=py26-oldest and py27-oldest), csr + # objects don't have a get_extensions() method, so we skip this test if + # the method isn't available. + if hasattr(csr, 'get_extensions'): + # NOTE: Ideally we would filter by the TLS Feature OID, but + # OpenSSL.crypto.X509Extension doesn't give us the extension's raw OID, + # and the shortname field is just "UNDEF" + must_staple_exts = [e for e in csr.get_extensions() + if e.get_data() == "0\x03\x02\x01\x05"] + self.assertEqual(len(must_staple_exts), 1, + "Expected exactly one Must Staple extension") + class ValidCSRTest(unittest.TestCase): """Tests for certbot.crypto_util.valid_csr.""" diff --git a/certbot/tests/hook_test.py b/certbot/tests/hook_test.py index ce78b5dc9..be7fb852d 100644 --- a/certbot/tests/hook_test.py +++ b/certbot/tests/hook_test.py @@ -56,14 +56,22 @@ class HookTest(unittest.TestCase): return mock_logger.warning def test_pre_hook(self): + hooks.pre_hook.already = False config = mock.MagicMock(pre_hook="true") self._test_a_hook(config, hooks.pre_hook, 1) config = mock.MagicMock(pre_hook="") self._test_a_hook(config, hooks.pre_hook, 0) def test_post_hook(self): + hooks.pre_hook.already = False + # if pre-hook isn't called, post-hook shouldn't be config = mock.MagicMock(post_hook="true", verb="splonk") + self._test_a_hook(config, hooks.post_hook, 0) + + config = mock.MagicMock(post_hook="true", verb="splonk") + self._test_a_hook(config, hooks.pre_hook, 1) self._test_a_hook(config, hooks.post_hook, 2) + config = mock.MagicMock(post_hook="true", verb="renew") self._test_a_hook(config, hooks.post_hook, 0) diff --git a/docs/ciphers.rst b/docs/ciphers.rst index ef644b7a0..8996dd9ef 100644 --- a/docs/ciphers.rst +++ b/docs/ciphers.rst @@ -17,15 +17,13 @@ Autoupdates Within certain limits, TLS server software can choose what kind of cryptography to use when a client connects. These choices can affect security, compatibility, and performance in complex ways. Most of -these options are independent of a particular certificate. The Let's -Encrypt client tries to provide defaults that we think are most useful -to our users. +these options are independent of a particular certificate. Certbot +tries to provide defaults that we think are most useful to our users. -As described below, the Let's Encrypt client will default to modifying +As described below, Certbot will default to modifying server software's cryptographic settings to keep these up-to-date with -what we think are appropriate defaults when new versions of the Let's -Encrypt client are installed (for example, by an operating system package -manager). +what we think are appropriate defaults when new versions of the Certbot +are installed (for example, by an operating system package manager). When this feature is implemented, this document will be updated to describe how to disable these automatic changes. @@ -54,7 +52,7 @@ improve, others' security. But important information that improves our understanding of the state of the art is published regularly. When enabling TLS support in a compatible web server (which is a separate -step from obtaining a certificate), Let's Encrypt has the ability to +step from obtaining a certificate), Certbot has the ability to update that web server's TLS configuration. Again, this is *different from the cryptographic particulars of the certificate itself*; the certificate as of the initial release will be RSA-signed using one of @@ -80,30 +78,29 @@ art. However, the Let's Encrypt certificate authority does *not* dictate end-users' security policy, and any site is welcome to change its preferences in accordance with its own policy or its administrators' preferences, and use different cryptographic mechanisms or parameters, -or a different priority order, than the defaults provided by the Let's -Encrypt client. +or a different priority order, than the defaults provided by Certbot. -If you don't use the Let's Encrypt client to configure your server -directly, because the client doesn't integrate with your server software -or because you chose not to use this integration, then the cryptographic -defaults haven't been modified, and the cryptography chosen by the server -will still be whatever the default for your software was. For example, -if you obtain a certificate using *standalone* mode and then manually -install it in an IMAP or LDAP server, your cryptographic settings will -not be modified by the client in any way. +If you don't use Certbot to configure your server directly, because the +client doesn't integrate with your server software or because you chose +not to use this integration, then the cryptographic defaults haven't been +modified, and the cryptography chosen by the server will still be whatever +the default for your software was. For example, if you obtain a +certificate using *standalone* mode and then manually install it in an IMAP +or LDAP server, your cryptographic settings will not be modified by the +client in any way. Sources of defaults ------------------- -Initially, the Let's Encrypt client will configure users' servers to -use the cryptographic defaults recommended by the Mozilla project. -These settings are well-reasoned recommendations that carefully -consider client software compatibility. They are described at +Initially, Certbot will configure users' servers to use the cryptographic +defaults recommended by the Mozilla project. These settings are well-reasoned +recommendations that carefully consider client software compatibility. They +are described at https://wiki.mozilla.org/Security/Server_Side_TLS -and the version implemented by the Let's Encrypt client will be the +and the version implemented by Certbot will be the version that was most current as of the release date of each client version. Mozilla offers three separate sets of cryptographic options, which trade off security and compatibility differently. These are @@ -113,12 +110,12 @@ to most-backwards compatible). The client will follow the Mozilla defaults for the *Intermediate* configuration by default, at least with regards to ciphersuites and TLS versions. Mozilla's web site describes which client software will be compatible with each configuration. You can also use -the Qualys SSL Labs site, which the Let's Encrypt software will suggest +the Qualys SSL Labs site, which Certbot will suggest when installing a certificate, to test your server and see whether it will be compatible with particular software versions. -It will be possible to ask the Let's Encrypt client to instead apply -(and track) Modern or Old configurations. +It will be possible to ask Certbot to instead apply (and track) Modern +or Old configurations. The Let's Encrypt project expects to follow the Mozilla recommendations in the future as those recommendations are updated. (For example, some @@ -127,15 +124,15 @@ which uses the ChaCha and Poly1305 algorithms, and which is already implemented by the Chrome browser. Mozilla has delayed recommending ``0xcc13`` over compatibility and standardization concerns, but is likely to recommend it in the future once these concerns have been addressed. At -that point, the Let's Encrypt client would likely follow the Mozilla -recommendations and favor the use of this ciphersuite as well.) +that point, Certbot would likely follow the Mozilla recommendations and favor +the use of this ciphersuite as well.) The Let's Encrypt project may deviate from the Mozilla recommendations in the future if good cause is shown and we believe our users' priorities would be well-served by doing so. In general, please address relevant proposals for changing priorities to the Mozilla security -team first, before asking the Let's Encrypt project to change the -client's priorities. The Mozilla security team is likely to have more +team first, before asking the Certbot developers to change +Certbot's priorities. The Mozilla security team is likely to have more resources and expertise to bring to bear on evaluating reasons why its recommendations should be updated. @@ -144,8 +141,8 @@ small number of alternative configurations (apart from Modern, Intermediate, and Old) that there's reason to believe would be widely used by sysadmins; this would usually be a preferable course to modifying an existing configuration. For example, if many sysadmins want their -servers configured to track a different expert recommendation, Let's -Encrypt could add an option to do so. +servers configured to track a different expert recommendation, Certbot +could add an option to do so. Resources for recommendations @@ -156,9 +153,9 @@ recommendations with sources of expert guidance on ciphersuites and other cryptographic parameters. We're grateful to everyone who contributed suggestions. The recommendations we received are available at -https://github.com/letsencrypt/letsencrypt/wiki/Ciphersuite-guidance +https://github.com/certbot/certbot/wiki/Ciphersuite-guidance -Let's Encrypt client users are welcome to review these authorities to +Certbot users are welcome to review these authorities to better inform their own cryptographic parameter choices. We also welcome suggestions of other resources to add to this list. Please keep in mind that different recommendations may reflect different priorities @@ -172,26 +169,25 @@ This will probably look something like .. code-block:: shell - letsencrypt --cipher-recommendations mozilla-secure - letsencrypt --cipher-recommendations mozilla-intermediate - letsencrypt --cipher-recommendations mozilla-old + certbot --cipher-recommendations mozilla-secure + certbot --cipher-recommendations mozilla-intermediate + certbot --cipher-recommendations mozilla-old to track Mozilla's *Secure*, *Intermediate*, or *Old* recommendations, and .. code-block:: shell - letsencrypt --update-ciphers on + certbot --update-ciphers on -to enable updating ciphers with each new Let's Encrypt client release, -or +to enable updating ciphers with each new Certbot release, or .. code-block:: shell - letsencrypt --update-ciphers off + certbot --update-ciphers off to disable automatic configuration updates. These features have not yet -been implemented and this syntax may change then they are implemented. +been implemented and this syntax may change when they are implemented. TODO @@ -200,7 +196,7 @@ TODO The status of this feature is tracked as part of issue #1123 in our bug tracker. -https://github.com/letsencrypt/letsencrypt/issues/1123 +https://github.com/certbot/certbot/issues/1123 Prior to implementation of #1123, the client does not actually modify ciphersuites (this is intended to be implemented as a "configuration diff --git a/docs/cli-help.txt b/docs/cli-help.txt new file mode 100644 index 000000000..cb4bace58 --- /dev/null +++ b/docs/cli-help.txt @@ -0,0 +1,340 @@ +usage: + certbot [SUBCOMMAND] [options] [-d domain] [-d domain] ... + +Certbot can obtain and install HTTPS/TLS/SSL certificates. By default, +it will attempt to use a webserver both for obtaining and installing the +cert. Major SUBCOMMANDS are: + + (default) run Obtain & install a cert in your current webserver + certonly Obtain cert, but do not install it (aka "auth") + install Install a previously obtained cert in a server + renew Renew previously obtained certs that are near expiry + revoke Revoke a previously obtained certificate + rollback Rollback server configuration changes made during install + config_changes Show changes made to server config during installation + plugins Display information about installed plugins + +optional arguments: + -h, --help show this help message and exit + -c CONFIG_FILE, --config CONFIG_FILE + config file path (default: None) + -v, --verbose This flag can be used multiple times to incrementally + increase the verbosity of output, e.g. -vvv. (default: + -3) + -t, --text Use the text output instead of the curses UI. + (default: False) + -n, --non-interactive, --noninteractive + Run without ever asking for user input. This may + require additional command line flags; the client will + try to explain which ones are required if it finds one + missing (default: False) + --dry-run Perform a test run of the client, obtaining test + (invalid) certs but not saving them to disk. This can + currently only be used with the 'certonly' and 'renew' + subcommands. Note: Although --dry-run tries to avoid + making any persistent changes on a system, it is not + completely side-effect free: if used with webserver + authenticator plugins like apache and nginx, it makes + and then reverts temporary config changes in order to + obtain test certs, and reloads webservers to deploy + and then roll back those changes. It also calls --pre- + hook and --post-hook commands if they are defined + because they may be necessary to accurately simulate + renewal. --renew-hook commands are not called. + (default: False) + --register-unsafely-without-email + Specifying this flag enables registering an account + with no 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 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. (default: False) + -m EMAIL, --email EMAIL + Email used for registration and recovery contact. + (default: None) + -d DOMAIN, --domains DOMAIN, --domain DOMAIN + Domain names to apply. For multiple domains you can + use multiple -d flags or enter a comma separated list + of domains as a parameter. (default: []) + --user-agent USER_AGENT + Set a custom user agent string for the client. User + agent strings allow the CA to collect high level + statistics about success rates by OS and plugin. If + you wish to hide your server OS version from the Let's + Encrypt server, set this to "". (default: None) + +automation: + Arguments for automating execution & other tweaks + + --keep-until-expiring, --keep, --reinstall + If the requested cert matches an existing cert, always + keep the existing one until it is due for renewal (for + the 'run' subcommand this means reinstall the existing + cert) (default: False) + --expand If an existing cert covers some subset of the + requested names, always expand and replace it with the + additional names. (default: False) + --version show program's version number and exit + --force-renewal, --renew-by-default + If a certificate already exists for the requested + domains, renew it now, regardless of whether it is + near expiry. (Often --keep-until-expiring is more + appropriate). Also implies --expand. (default: False) + --allow-subset-of-names + When performing domain validation, do not consider it + a failure if authorizations can not be obtained for a + strict subset of the requested domains. This may be + useful for allowing renewals for multiple domains to + succeed even if some domains no longer point at this + system. This option cannot be used with --csr. + (default: False) + --agree-tos Agree to the ACME Subscriber Agreement (default: + False) + --account ACCOUNT_ID Account ID to use (default: None) + --duplicate Allow making a certificate lineage that duplicates an + existing one (both can be renewed in parallel) + (default: False) + --os-packages-only (letsencrypt-auto only) install OS package + dependencies and then stop (default: False) + --no-self-upgrade (letsencrypt-auto only) prevent the letsencrypt-auto + script from upgrading itself to newer released + versions (default: False) + -q, --quiet Silence all output except errors. Useful for + automation via cron. Implies --non-interactive. + (default: False) + +testing: + The following flags are meant for testing purposes only! Do NOT change + them, unless you really know what you're doing! + + --debug Show tracebacks in case of errors, and allow + letsencrypt-auto execution on experimental platforms + (default: False) + --no-verify-ssl Disable SSL certificate verification. (default: False) + --tls-sni-01-port TLS_SNI_01_PORT + Port number to perform tls-sni-01 challenge. Boulder + in testing mode defaults to 5001. (default: 443) + --http-01-port HTTP01_PORT + Port used in the SimpleHttp challenge. (default: 80) + --break-my-certs Be willing to replace or renew valid certs with + invalid (testing/staging) certs (default: False) + --test-cert, --staging + Use the staging server to obtain test (invalid) certs; + equivalent to --server https://acme- + staging.api.letsencrypt.org/directory (default: False) + +security: + Security parameters & server settings + + --rsa-key-size N Size of the RSA key. (default: 2048) + --redirect Automatically redirect all HTTP traffic to HTTPS for + the newly authenticated vhost. (default: None) + --no-redirect Do not automatically redirect all HTTP traffic to + HTTPS for the newly authenticated vhost. (default: + None) + --hsts Add the Strict-Transport-Security header to every HTTP + response. Forcing browser to use always use SSL for + the domain. Defends against SSL Stripping. (default: + False) + --no-hsts Do not automatically add the Strict-Transport-Security + header to every HTTP response. (default: False) + --uir Add the "Content-Security-Policy: upgrade-insecure- + requests" header to every HTTP response. Forcing the + browser to use https:// for every http:// resource. + (default: None) + --no-uir Do not automatically set the "Content-Security-Policy: + upgrade-insecure-requests" header to every HTTP + response. (default: None) + --strict-permissions Require that all configuration files are owned by the + current user; only needed if your config is somewhere + unsafe like /tmp/ (default: False) + +renew: + The 'renew' subcommand will attempt to renew all certificates (or more + precisely, certificate lineages) you have previously obtained if they are + close to expiry, and print a summary of the results. By default, 'renew' + will reuse the options used to create obtain or most recently successfully + renew each certificate lineage. You can try it with `--dry-run` first. For + more fine-grained control, you can renew individual lineages with the + `certonly` subcommand. Hooks are available to run commands before and + after renewal; see https://certbot.eff.org/docs/using.html#renewal for + more information on these. + + --pre-hook PRE_HOOK Command to be run in a shell before obtaining any + certificates. Intended primarily for renewal, where it + can be used to temporarily shut down a webserver that + might conflict with the standalone plugin. This will + only be called if a certificate is actually to be + obtained/renewed. (default: None) + --post-hook POST_HOOK + Command to be run in a shell after attempting to + obtain/renew certificates. Can be used to deploy + renewed certificates, or to restart any servers that + were stopped by --pre-hook. (default: None) + --renew-hook RENEW_HOOK + Command to be run in a shell once for each + successfully renewed certificate.For this command, the + shell variable $RENEWED_LINEAGE will point to + theconfig live subdirectory containing the new certs + and keys; the shell variable $RENEWED_DOMAINS will + contain a space-delimited list of renewed cert domains + (default: None) + +certonly: + Options for modifying how a cert is obtained + + --csr CSR Path to a Certificate Signing Request (CSR) in DER + format; note that the .csr file *must* contain a + Subject Alternative Name field for each domain you + want certified. Currently --csr only works with the + 'certonly' subcommand' (default: None) + +install: + Options for modifying how a cert is deployed + +revoke: + Options for revocation of certs + +rollback: + Options for reverting config changes + + --checkpoints N Revert configuration N number of checkpoints. + (default: 1) + +plugins: + Plugin options + + --init Initialize plugins. (default: False) + --prepare Initialize and prepare plugins. (default: False) + --authenticators Limit to authenticator plugins only. (default: None) + --installers Limit to installer plugins only. (default: None) + +config_changes: + Options for showing a history of config changes + + --num NUM How many past revisions you want to be displayed + (default: None) + +paths: + Arguments changing execution paths & servers + + --cert-path CERT_PATH + Path to where cert is saved (with auth --csr), + installed from or revoked. (default: None) + --key-path KEY_PATH Path to private key for cert installation or + revocation (if account key is missing) (default: None) + --fullchain-path FULLCHAIN_PATH + Accompanying path to a full certificate chain (cert + plus chain). (default: None) + --chain-path CHAIN_PATH + Accompanying path to a certificate chain. (default: + None) + --config-dir CONFIG_DIR + Configuration directory. (default: /etc/letsencrypt) + --work-dir WORK_DIR Working directory. (default: /var/lib/letsencrypt) + --logs-dir LOGS_DIR Logs directory. (default: /var/log/letsencrypt) + --server SERVER ACME Directory Resource URI. (default: + https://acme-v01.api.letsencrypt.org/directory) + +plugins: + Certbot client supports an extensible plugins architecture. See 'certbot + plugins' for a list of all installed plugins and their names. You can + force a particular plugin by setting options provided below. Running + --help will list flags specific to that plugin. + + -a AUTHENTICATOR, --authenticator AUTHENTICATOR + Authenticator plugin name. (default: None) + -i INSTALLER, --installer INSTALLER + Installer plugin name (also used to find domains). + (default: None) + --configurator CONFIGURATOR + Name of the plugin that is both an authenticator and + an installer. Should not be used together with + --authenticator or --installer. (default: None) + --apache Obtain and install certs using Apache (default: False) + --nginx Obtain and install certs using Nginx (default: False) + --standalone Obtain certs using a "standalone" webserver. (default: + False) + --manual Provide laborious manual instructions for obtaining a + cert (default: False) + --webroot Obtain certs by placing files in a webroot directory. + (default: False) + +nginx: + Nginx Web Server - currently doesn't work + + --nginx-server-root NGINX_SERVER_ROOT + Nginx server root directory. (default: /etc/nginx) + --nginx-ctl NGINX_CTL + Path to the 'nginx' binary, used for 'configtest' and + retrieving nginx version number. (default: nginx) + +standalone: + Automatically use a temporary webserver + + --standalone-supported-challenges STANDALONE_SUPPORTED_CHALLENGES + Supported challenges. Preferred in the order they are + listed. (default: tls-sni-01,http-01) + +manual: + Manually configure an HTTP server + + --manual-test-mode Test mode. Executes the manual command in subprocess. + (default: False) + --manual-public-ip-logging-ok + Automatically allows public IP logging. (default: + False) + +webroot: + Place files in webroot directory + + --webroot-path WEBROOT_PATH, -w WEBROOT_PATH + public_html / webroot path. This can be specified + multiple times to handle different domains; each + domain will have the webroot path that 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` (default: []) + --webroot-map WEBROOT_MAP + JSON dictionary mapping domains to webroot paths; this + implies -d for each entry. You may need to escape this + from your shell. E.g.: --webroot-map + '{"eg1.is,m.eg1.is":"/www/eg1/", "eg2.is":"/www/eg2"}' + This option is merged with, but takes precedence over, + -w / -d entries. At present, if you put webroot-map in + a config file, it needs to be on a single line, like: + webroot-map = {"example.com":"/var/www"}. (default: + {}) + +apache: + Apache Web Server - Alpha + + --apache-enmod APACHE_ENMOD + Path to the Apache 'a2enmod' binary. (default: + a2enmod) + --apache-dismod APACHE_DISMOD + Path to the Apache 'a2dismod' binary. (default: + a2dismod) + --apache-le-vhost-ext APACHE_LE_VHOST_EXT + SSL vhost configuration extension. (default: -le- + ssl.conf) + --apache-server-root APACHE_SERVER_ROOT + Apache server root directory. (default: /etc/apache2) + --apache-vhost-root APACHE_VHOST_ROOT + Apache server VirtualHost configuration root (default: + /etc/apache2/sites-available) + --apache-challenge-location APACHE_CHALLENGE_LOCATION + Directory path for challenge configuration. (default: + /etc/apache2) + --apache-handle-modules APACHE_HANDLE_MODULES + Let installer handle enabling required modules for + you.(Only Ubuntu/Debian currently) (default: True) + --apache-handle-sites APACHE_HANDLE_SITES + Let installer handle enabling sites for you.(Only + Ubuntu/Debian currently) (default: True) + +null: + Null Installer diff --git a/docs/conf.py b/docs/conf.py index fb2bdea73..e387e1eae 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -64,8 +64,8 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'Let\'s Encrypt' -copyright = u'2014-2015, Let\'s Encrypt Project' +project = u'Certbot' +copyright = u'2014-2016 - The Certbot software and documentation are licensed under the Apache 2.0 license as described at https://eff.org/cb-license ' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -225,7 +225,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'LetsEncryptdoc' +htmlhelp_basename = 'Certbotdoc' # -- Options for LaTeX output --------------------------------------------- @@ -247,8 +247,8 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'LetsEncrypt.tex', u'Let\'s Encrypt Documentation', - u'Let\'s Encrypt Project', 'manual'), + ('index', 'Certbot.tex', u'Certbot Documentation', + u'Certbot Project', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -277,7 +277,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'certbot', u'Let\'s Encrypt Documentation', + ('index', 'certbot', u'Certbot Documentation', [project], 7), ('man/certbot', 'certbot', u'certbot script documentation', [project], 1), @@ -293,8 +293,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'LetsEncrypt', u'Let\'s Encrypt Documentation', - u'Let\'s Encrypt Project', 'LetsEncrypt', 'One line description of project.', + ('index', 'Certbot', u'Certbot Documentation', + u'Certbot Project', 'Certbot', 'One line description of project.', 'Miscellaneous'), ] diff --git a/docs/contributing.rst b/docs/contributing.rst index 216cd4888..3318ec103 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -20,8 +20,8 @@ once: .. code-block:: shell - git clone https://github.com/letsencrypt/letsencrypt - cd letsencrypt + git clone https://github.com/certbot/certbot + cd certbot ./letsencrypt-auto-source/letsencrypt-auto --os-packages-only ./tools/venv.sh @@ -57,8 +57,8 @@ your pull request must have thorough unit test coverage, pass our `integration`_ tests, and be compliant with the :ref:`coding style `. -.. _github issue tracker: https://github.com/letsencrypt/letsencrypt/issues -.. _Good Volunteer Task: https://github.com/letsencrypt/letsencrypt/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 +.. _github issue tracker: https://github.com/certbot/certbot/issues +.. _Good Volunteer Task: https://github.com/certbot/certbot/issues?q=is%3Aopen+is%3Aissue+label%3A%22Good+Volunteer+Task%22 Testing ------- @@ -175,8 +175,8 @@ Configurators may implement just one of those). There are also `~certbot.interfaces.IDisplay` plugins, which implement bindings to alternative UI libraries. -.. _interfaces.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/interfaces.py -.. _plugins/common.py: https://github.com/letsencrypt/letsencrypt/blob/master/certbot/plugins/common.py#L34 +.. _interfaces.py: https://github.com/certbot/certbot/blob/master/certbot/interfaces.py +.. _plugins/common.py: https://github.com/certbot/certbot/blob/master/certbot/plugins/common.py#L34 Authenticators @@ -297,7 +297,7 @@ Please: 4. Remember to use ``pylint``. .. _Google Python Style Guide: - https://google-styleguide.googlecode.com/svn/trunk/pyguide.html + https://google.github.io/styleguide/pyguide.html .. _Sphinx-style: http://sphinx-doc.org/ .. _PEP 8 - Style Guide for Python Code: https://www.python.org/dev/peps/pep-0008 @@ -323,7 +323,7 @@ Steps: See `Known Issues`_. If it's not a known issue, fix any errors. .. _Known Issues: - https://github.com/letsencrypt/letsencrypt/wiki/Known-issues + https://github.com/certbot/certbot/wiki/Known-issues Updating the documentation ========================== @@ -333,7 +333,7 @@ commands: .. code-block:: shell - make -C docs clean html + make -C docs clean html man This should generate documentation in the ``docs/_build/html`` directory. diff --git a/docs/index.rst b/docs/index.rst index 68289d760..b541e376e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,4 @@ -Welcome to the Let's Encrypt client documentation! +Welcome to the Certbot documentation! ================================================== .. toctree:: diff --git a/docs/intro.rst b/docs/intro.rst index 188ff4302..2fffbec68 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -1,6 +1,6 @@ -============ -Introduction -============ +===================== +README / Introduction +===================== .. include:: ../README.rst .. include:: ../CHANGES.rst diff --git a/docs/man/certbot.rst b/docs/man/certbot.rst index 7382d7811..8fb03db49 100644 --- a/docs/man/certbot.rst +++ b/docs/man/certbot.rst @@ -1 +1 @@ -.. program-output:: certbot --help all +.. literalinclude:: cli-help.txt diff --git a/docs/packaging.rst b/docs/packaging.rst index 5f09b65fa..bd366dbaa 100644 --- a/docs/packaging.rst +++ b/docs/packaging.rst @@ -3,4 +3,4 @@ Packaging Guide =============== Documentation can be found at -https://github.com/letsencrypt/letsencrypt/wiki/Packaging. +https://github.com/certbot/certbot/wiki/Packaging. diff --git a/docs/using.rst b/docs/using.rst index e7e0e9474..b10532259 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -5,66 +5,38 @@ User Guide .. contents:: Table of Contents :local: -.. _installation: +Getting Certbot +=============== -Installation -============ +To get specific instructions for installing Certbot on your OS, we recommend +visiting certbot.eff.org_. If you're offline, you can find some general +instructions `in the README / Introduction `__ -.. _letsencrypt-auto: +__ installation_ +.. _certbot.eff.org: https://certbot.eff.org -letsencrypt-auto ----------------- +.. _certbot-auto: -``letsencrypt-auto`` is a wrapper which installs some dependencies -from your OS standard package repositories (e.g. using `apt-get` or -`yum`), and for other dependencies it sets up a virtualized Python -environment with packages downloaded from PyPI [#venv]_. It also -provides automated updates. +The name of the certbot command +------------------------------- -To install and run the client, just type... - -.. code-block:: shell - - ./letsencrypt-auto - -.. hint:: During the beta phase, Let's Encrypt enforces strict rate limits on - the number of certificates issued for one domain. It is recommended to - initially use the test server via `--test-cert` until you get the desired - certificates. - -Throughout the documentation, whenever you see references to -``letsencrypt`` script/binary, you can substitute in -``letsencrypt-auto``. For example, to get basic help you would type: - -.. code-block:: shell - - ./letsencrypt-auto --help - -or for full help, type: - -.. code-block:: shell - - ./letsencrypt-auto --help all - - -``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, -Arch Linux, Gentoo, FreeBSD, and OpenBSD 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. +Many platforms now have native packages that give you a ``certbot`` or (for +older packages) ``letsencrypt`` command you can run. On others, the +``certbot-auto`` / ``letsencrypt-auto`` installer and wrapper script is a +stand-in. Throughout the documentation, whenever you see references to +``certbot`` script/binary, you should substitute in the name of the command +that certbot.eff.org_ told you to use on your system (``certbot``, +``letsencrypt``, or ``certbot-auto``). Plugins ======= -The Let's Encrypt client supports a number of different "plugins" that can be +The Certbot 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. +can be used with the "certbot run" command, which is the default. =========== ==== ==== =============================================================== Plugin Auth Inst Notes @@ -79,27 +51,10 @@ standalone_ Y N Uses a "standalone" webserver to obtain a cert. Requires webserver is not supported or not desired. 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_. +nginx_ Y Y Very experimental and not included in certbot-auto_. =========== ==== ==== =============================================================== -There are also a number of third-party plugins for the client, provided by other developers: - -=========== ==== ==== =============================================================== -Plugin Auth Inst Notes -=========== ==== ==== =============================================================== -plesk_ Y Y Integration with the Plesk web hosting tool -haproxy_ Y Y Integration with the HAProxy load balancer -s3front_ Y Y Integration with Amazon CloudFront distribution of S3 buckets -gandi_ Y Y Integration with Gandi's hosting products and API -=========== ==== ==== =============================================================== - -.. _plesk: https://github.com/plesk/letsencrypt-plesk -.. _haproxy: https://code.greenhost.net/open/letsencrypt-haproxy -.. _s3front: https://github.com/dlapiduz/letsencrypt-s3front -.. _gandi: https://github.com/Gandi/letsencrypt-gandi - -Future plugins for IMAP servers, SMTP servers, IRC servers, etc, are likely to -be installers but not authenticators. +There are many third-party-plugins_ available. Apache ------ @@ -130,16 +85,16 @@ specified ``--webroot-path``. So, for instance, :: - letsencrypt certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net + certbot certonly --webroot -w /var/www/example/ -d www.example.com -d example.com -w /var/www/other -d other.example.net -d another.other.example.net would obtain a single certificate for all of those names, using the ``/var/www/example`` webroot directory for the first two, and ``/var/www/other`` for the second two. The webroot plugin works by creating a temporary file for each of your requested -domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's -Encrypt validation server makes HTTP requests to validate that the DNS for each -requested domain resolves to the server running letsencrypt. An example request +domains in ``${webroot-path}/.well-known/acme-challenge``. Then the Let's Encrypt +validation server makes HTTP requests to validate that the DNS for each +requested domain resolves to the server running certbot. An example request made to your web server would look like: :: @@ -173,7 +128,7 @@ specified port using each requested domain name. Manual ------ -If you'd like to obtain a cert running ``letsencrypt`` on a machine +If you'd like to obtain a cert running ``certbot`` on a machine other than your target webserver or perform the steps for domain validation yourself, you can use the manual plugin. While hidden from the UI, you can use the plugin to obtain a cert by specifying @@ -186,16 +141,49 @@ Nginx In the future, if you're running Nginx you will hopefully be able to use this plugin to automatically obtain and install your certificate. The Nginx plugin is -still experimental, however, and is not installed with letsencrypt-auto_. If +still experimental, however, and is not installed with certbot-auto_. If installed, you can select this plugin on the command line by including ``--nginx``. +.. _third-party-plugins: + Third-party plugins ------------------- -These plugins are listed at -https://github.com/letsencrypt/letsencrypt/wiki/Plugins. If you're -interested, you can also :ref:`write your own plugin `. +There are also a number of third-party plugins for the client, provided by +other developers. Many are beta/experimental, but some are already in +widespread use: + +=========== ==== ==== =============================================================== +Plugin Auth Inst Notes +=========== ==== ==== =============================================================== +plesk_ Y Y Integration with the Plesk web hosting tool +haproxy_ Y Y Integration with the HAProxy load balancer +s3front_ Y Y Integration with Amazon CloudFront distribution of S3 buckets +gandi_ Y Y Integration with Gandi's hosting products and API +varnish_ Y N Obtain certs via a Varnish server +external_ Y N A plugin for convenient scripting (See also ticket 2782_) +icecast_ N Y Deploy certs to Icecast 2 streaming media servers +pritunl_ N Y Install certs in pritunl distributed OpenVPN servers +proxmox_ N Y Install certs in Proxmox Virtualization servers +postfix_ N Y STARTTLS Everywhere is becoming a Certbot Postfix/Exim plugin +=========== ==== ==== =============================================================== + +.. _plesk: https://github.com/plesk/letsencrypt-plesk +.. _haproxy: https://code.greenhost.net/open/letsencrypt-haproxy +.. _s3front: https://github.com/dlapiduz/letsencrypt-s3front +.. _gandi: https://github.com/Gandi/letsencrypt-gandi +.. _icecast: https://github.com/e00E/lets-encrypt-icecast +.. _varnish: http://git.sesse.net/?p=letsencrypt-varnish-plugin +.. _2782: https://github.com/certbot/certbot/issues/2782 +.. _pritunl: https://github.com/kharkevich/letsencrypt-pritunl +.. _proxmox: https://github.com/kharkevich/letsencrypt-proxmox +.. _external: https://github.com/marcan/letsencrypt-external +.. _postfix: https://github.com/EFForg/starttls-everywhere + +If you're interested, you can also :ref:`write your own plugin `. + + Renewal ======= @@ -204,11 +192,11 @@ Renewal days). Make sure you renew the certificates at least once in 3 months. -The ``letsencrypt`` client now supports a ``renew`` action to check +The ``certbot`` client now supports a ``renew`` action to check all installed certificates for impending expiry and attempt to renew them. The simplest form is simply -``letsencrypt renew`` +``certbot renew`` This will attempt to renew any previously-obtained certificates that expire in less than 30 days. The same plugin and options that were used @@ -219,14 +207,14 @@ You can also specify hooks to be run before or after a certificate is renewed. For example, if you want to use the standalone_ plugin to renew your certificates, you may want to use a command like -``letsencrypt renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"`` +``certbot renew --standalone --pre-hook "service nginx stop" --post-hook "service nginx start"`` This will stop Nginx so standalone can bind to the necessary ports and then restart Nginx after the plugin is finished. The hooks will only be run if a certificate is due for renewal, so you can run this command frequently without unnecessarily stopping your webserver. More information about renewal hooks can be found by running -``letsencrypt --help renew``. +``certbot --help renew``. If you're sure that this command executes successfully without human intervention, you can add the command to ``crontab`` (since certificates @@ -242,9 +230,9 @@ certificate regardless of its age. (This form is not appropriate to run daily because each certificate will be renewed every day, which will quickly run into the certificate authority rate limit.) -Note that options provided to ``letsencrypt renew`` will apply to +Note that options provided to ``certbot renew`` will apply to *every* certificate for which renewal is attempted; for example, -``letsencrypt renew --rsa-key-size 4096`` would try to replace every +``certbot renew --rsa-key-size 4096`` would try to replace every near-expiry certificate with an equivalent certificate using a 4096-bit RSA public key. If a certificate is successfully renewed using specified options, those options will be saved and used for future @@ -253,12 +241,12 @@ renewals of that certificate. An alternative form that provides for more fine-grained control over the renewal process (while renewing specified certificates one at a time), -is ``letsencrypt certonly`` with the complete set of subject domains of +is ``certbot certonly`` with the complete set of subject domains of a specific certificate specified via `-d` flags. You may also want to include the ``-n`` or ``--noninteractive`` flag to prevent blocking on user input (which is useful when running the command from cron). -``letsencrypt certonly -n -d example.com -d www.example.com`` +``certbot certonly -n -d example.com -d www.example.com`` (All of the domains covered by the certificate must be specified in this case in order to renew and replace the old certificate rather @@ -271,21 +259,25 @@ The ``certonly`` form attempts to renew one individual certificate. 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. -Let's Encrypt is working hard on improving the renewal process, and we +Certbot is working hard on improving the renewal process, and we apologize for any inconveniences you encounter in integrating these commands into your individual environment. +.. _command-line: + +Command line options +==================== + +Certbot supports a lot of command line options. Here's the full list, from +``certbot --help all``: + +.. literalinclude:: cli-help.txt .. _where-certs: Where are my certificates? ========================== -First of all, we encourage you to use Apache or nginx installers, both -which perform the certificate management automatically. If, however, -you prefer to manage everything by hand, this section provides -information on where to find necessary files. - All generated keys and issued certificates can be found in ``/etc/letsencrypt/live/$domain``. Rather than copying, please point your (web) server configuration directly to those files (or create @@ -302,7 +294,7 @@ The following files are available: Private key for the certificate. .. warning:: This **must be kept secret at all times**! Never share - it with anyone, including Let's Encrypt developers. You cannot + it with anyone, including Certbot developers. You cannot put it into a safe, however - your server still needs to access this file in order for SSL/TLS to work. @@ -345,8 +337,8 @@ will cause nasty errors served through the browsers! .. note:: All files are PEM-encoded (as the filename suffix suggests). If you need other format, such as DER or PFX, then you - could convert using ``openssl``, but this means you will not - benefit from automatic renewal_! + could convert using ``openssl``. You can automate that with + ``--renew-hook`` if you're using automatic renewal_. .. _config-file: @@ -355,7 +347,7 @@ Configuration file ================== It is possible to specify configuration file with -``letsencrypt-auto --config cli.ini`` (or shorter ``-c cli.ini``). An +``certbot-auto --config cli.ini`` (or shorter ``-c cli.ini``). An example configuration file is shown below: .. include:: ../examples/cli.ini @@ -374,13 +366,14 @@ By default, the following locations are searched: Getting help ============ -If you're having problems you can chat with us on `IRC (#letsencrypt @ -Freenode) `_ or -get support on our `forums `_. +If you're having problems you can chat with us on `IRC (#certbot @ +OFTC) `_ or at +`IRC (#letsencrypt @ freenode) `_ +or get support on the Let's Encrypt `forums `_. If you find a bug in the software, please do report it in our `issue tracker -`_. Remember to +`_. Remember to give us as much information as possible: - copy and paste exact command line used and the output (though mind @@ -388,9 +381,9 @@ give us as much information as possible: information, including your email and domains) - copy and paste logs from ``/var/log/letsencrypt`` (though mind they also might contain personally identifiable information) -- copy and paste ``letsencrypt --version`` output +- copy and paste ``certbot --version`` output - your operating system, including specific version -- specify which installation_ method you've chosen +- specify which installation method you've chosen Other methods of installation ============================= @@ -405,10 +398,10 @@ 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 +manually. https://github.com/certbot/certbot/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 +certbot-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, @@ -417,7 +410,7 @@ to, `install Docker`_, then issue the following command: .. code-block:: shell - sudo docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \ + sudo docker run -it --rm -p 443:443 -p 80:80 --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ quay.io/letsencrypt/letsencrypt:latest auth @@ -447,19 +440,19 @@ Operating System Packages .. code-block:: shell - sudo pacman -S letsencrypt letsencrypt-apache + sudo pacman -S letsencrypt **Debian** -If you run Debian Stretch or Debian Sid, you can install letsencrypt packages. +If you run Debian Stretch or Debian Sid, you can install certbot packages. .. code-block:: shell sudo apt-get update - sudo apt-get install letsencrypt python-letsencrypt-apache + sudo apt-get install certbot python-certbot-apache If you don't want to use the Apache plugin, you can omit the -``python-letsencrypt-apache`` package. +``python-certbot-apache`` package. Packages exist for Debian Jessie via backports. First you'll have to follow the instructions at http://backports.debian.org/Instructions/ to enable the Jessie backports @@ -477,7 +470,7 @@ repo, if you have not already done so. Then run: **Gentoo** -The official Let's Encrypt client is available in Gentoo Portage. If you +The official Certbot client is available in Gentoo Portage. If you want to use the Apache plugin, it has to be installed separately: .. code-block:: shell @@ -486,8 +479,12 @@ want to use the Apache plugin, it has to be installed separately: emerge -av app-crypt/letsencrypt-apache Currently, only the Apache plugin is included in Portage. However, if you -want the nginx plugin, you can use Layman to add the mrueg overlay which -does include the nginx plugin package: +Warning! +You can use Layman to add the mrueg overlay which does include a package for the +Certbot Nginx plugin, however, this plugin is known to be buggy and should only +be used with caution after creating a backup up your Nginx configuration. +We strongly recommend you use the app-crypt/letsencrypt package instead until +the Nginx plugin is ready. .. code-block:: shell @@ -524,7 +521,7 @@ Note: this change is not required for the other plugins. **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 +Certbot for your distribution of choice please have a look at the :doc:`packaging`. @@ -540,19 +537,19 @@ whole process is described in the :doc:`contributing`. 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! + Certbot 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 +Unless you have a very specific requirements, we kindly suggest that you use +the certbot-auto_ method. It's the fastest, the most thoroughly tested and the most reliable way of getting our software and the free -SSL certificates! +TLS/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 +installing Certbot directly with pip from PyPI or downloading a ZIP archive from GitHub may be technically possible but are not presently recommended or supported. diff --git a/letsencrypt-apache/setup.py b/letsencrypt-apache/setup.py index a52044f87..b94746150 100644 --- a/letsencrypt-apache/setup.py +++ b/letsencrypt-apache/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0.dev0' +version = '0.7.0.dev0' # This package is a simple shim around certbot-apache diff --git a/letsencrypt-auto b/letsencrypt-auto index 942fd8ea2..8c6e6c486 100755 --- a/letsencrypt-auto +++ b/letsencrypt-auto @@ -1,6 +1,6 @@ #!/bin/sh # -# Download and run the latest release version of the Let's Encrypt client. +# Download and run the latest release version of the Certbot client. # # NOTE: THIS SCRIPT IS AUTO-GENERATED AND SELF-UPDATING # @@ -19,11 +19,36 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.5.0" +LE_AUTO_VERSION="0.6.0" +BASENAME=$(basename $0) +USAGE="Usage: $BASENAME [OPTIONS] +A self-updating wrapper script for the Certbot ACME client. When run, updates +to both this script and certbot will be downloaded and installed. After +ensuring you have the latest versions installed, certbot will be invoked with +all arguments you have provided. + +Help for certbot itself cannot be provided until it is installed. + + --debug attempt experimental installation + -h, --help print this help + -n, --non-interactive, --noninteractive run without asking for user input + --no-self-upgrade do not download updates + --os-packages-only install OS dependencies and exit + -v, --verbose provide more output + +All arguments are accepted and forwarded to the Certbot client when run." + +while getopts ":hnv" arg; do + case $arg in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac +done -# This script takes the same arguments as the main letsencrypt program, but it -# additionally responds to --verbose (more output) and --debug (allow support -# for experimental platforms) for arg in "$@" ; do case "$arg" in --debug) @@ -34,25 +59,26 @@ for arg in "$@" ; do # Do not upgrade this script (also prevents client upgrades, because each # copy of the script pins a hash of the python client) NO_SELF_UPGRADE=1;; + --help) + HELP=1;; + --noninteractive|--non-interactive) + ASSUME_YES=1;; --verbose) VERBOSE=1;; - [!-]*|-*[!v]*|-) - # Anything that isn't -v, -vv, etc.: that is, anything that does not - # start with a -, contains anything that's not a v, or is just "-" - ;; - *) # -v+ remains. - VERBOSE=1;; esac done -# letsencrypt-auto needs root access to bootstrap OS dependencies, and -# letsencrypt itself needs root access for almost all modes of operation +# certbot-auto needs root access to bootstrap OS dependencies, and +# certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but # this script *can* be run as root (not recommended), or fall back to using # `su` +SUDO_ENV="" +export CERTBOT_AUTO="$0" if test "`id -u`" -ne "0" ; then if command -v sudo 1>/dev/null 2>&1; then SUDO=sudo + SUDO_ENV="CERTBOT_AUTO=$0" else echo \"sudo\" is not available, will use \"su\" for installation steps... # Because the parameters in `su -c` has to be a string, @@ -81,6 +107,12 @@ else SUDO= fi +if [ $BASENAME = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + ExperimentalBootstrap() { # Arguments: Platform name, bootstrap function name if [ "$DEBUG" = 1 ]; then @@ -151,30 +183,45 @@ BootstrapDebCommon() { augeas_pkg="libaugeas0 augeas-lenses" AUGVERSION=`apt-cache show --no-all-versions libaugeas0 | grep ^Version: | cut -d" " -f2` + if [ "$ASSUME_YES" = 1 ]; then + YES_FLAG="-y" + fi + AddBackportRepo() { # ARGS: BACKPORT_NAME="$1" BACKPORT_SOURCELINE="$2" + echo "To use the Apache Certbot plugin, augeas needs to be installed from $BACKPORT_NAME." if ! grep -v -e ' *#' /etc/apt/sources.list | grep -q "$BACKPORT_NAME" ; then # This can theoretically error if sources.list.d is empty, but in that case we don't care. if ! grep -v -e ' *#' /etc/apt/sources.list.d/* 2>/dev/null | grep -q "$BACKPORT_NAME"; then - /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." - sleep 1s - /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." - sleep 1s - /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." - sleep 1s - if echo $BACKPORT_NAME | grep -q wheezy ; then - /bin/echo '(Backports are only installed if explicitly requested via "apt-get install -t wheezy-backports")' + if [ "$ASSUME_YES" = 1 ]; then + /bin/echo -n "Installing augeas from $BACKPORT_NAME in 3 seconds..." + sleep 1s + /bin/echo -ne "\e[0K\rInstalling augeas from $BACKPORT_NAME in 2 seconds..." + sleep 1s + /bin/echo -e "\e[0K\rInstalling augeas from $BACKPORT_NAME in 1 second ..." + sleep 1s + add_backports=1 + else + read -p "Would you like to enable the $BACKPORT_NAME repository [Y/n]? " response + case $response in + [yY][eE][sS]|[yY]|"") + add_backports=1;; + *) + add_backports=0;; + esac + fi + if [ "$add_backports" = 1 ]; then + $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" + $SUDO apt-get update fi - - $SUDO sh -c "echo $BACKPORT_SOURCELINE >> /etc/apt/sources.list.d/$BACKPORT_NAME.list" - $SUDO apt-get update fi fi - $SUDO apt-get install -y --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg - augeas_pkg= - + if [ "$add_backports" != 0 ]; then + $SUDO apt-get install $YES_FLAG --no-install-recommends -t "$BACKPORT_NAME" $augeas_pkg + augeas_pkg= + fi } @@ -186,12 +233,12 @@ BootstrapDebCommon() { AddBackportRepo precise-backports "deb http://archive.ubuntu.com/ubuntu precise-backports main restricted universe multiverse" else echo "No libaugeas0 version is available that's new enough to run the" - echo "Let's Encrypt apache plugin..." + echo "Certbot apache plugin..." fi # XXX add a case for ubuntu PPAs fi - $SUDO apt-get install -y --no-install-recommends \ + $SUDO apt-get install $YES_FLAG --no-install-recommends \ python \ python-dev \ $virtualenv \ @@ -212,9 +259,10 @@ BootstrapDebCommon() { BootstrapRpmCommon() { # Tested with: - # - Fedora 22, 23 (x64) + # - Fedora 20, 21, 22, 23 (x64) # - Centos 7 (x64: on DigitalOcean droplet) # - CentOS 7 Minimal install in a Hyper-V VM + # - CentOS 6 (EPEL must be installed manually) if type dnf 2>/dev/null then @@ -228,54 +276,62 @@ BootstrapRpmCommon() { exit 1 fi + pkgs=" + gcc + dialog + augeas-libs + openssl + openssl-devel + libffi-devel + redhat-rpm-config + ca-certificates + " + # Some distros and older versions of current distros use a "python27" # instead of "python" naming convention. Try both conventions. - if ! $SUDO $tool install -y \ - python \ - python-devel \ - python-virtualenv \ - python-tools \ - python-pip - then - if ! $SUDO $tool install -y \ - python27 \ - python27-devel \ - python27-virtualenv \ - python27-tools \ - python27-pip - then - echo "Could not install Python dependencies. Aborting bootstrap!" - exit 1 - fi + if $SUDO $tool list python >/dev/null 2>&1; then + pkgs="$pkgs + python + python-devel + python-virtualenv + python-tools + python-pip + " + else + pkgs="$pkgs + python27 + python27-devel + python27-virtualenv + python27-tools + python27-pip + " fi - if ! $SUDO $tool install -y \ - gcc \ - dialog \ - augeas-libs \ - openssl \ - openssl-devel \ - libffi-devel \ - redhat-rpm-config \ - ca-certificates - then - echo "Could not install additional dependencies. Aborting bootstrap!" - exit 1 - fi - - if $SUDO $tool list installed "httpd" >/dev/null 2>&1; then - if ! $SUDO $tool install -y mod_ssl - then - echo "Apache found, but mod_ssl could not be installed." - fi + pkgs="$pkgs + mod_ssl + " + fi + + if [ "$ASSUME_YES" = 1 ]; then + yes_flag="-y" + fi + + if ! $SUDO $tool install $yes_flag $pkgs; then + echo "Could not install OS dependencies. Aborting bootstrap!" + exit 1 fi } BootstrapSuseCommon() { # SLE12 don't have python-virtualenv - $SUDO zypper -nq in -l \ + if [ "$ASSUME_YES" = 1 ]; then + zypper_flags="-nq" + install_flags="-l" + fi + + $SUDO zypper $zypper_flags in $install_flags \ python \ python-devel \ python-virtualenv \ @@ -310,8 +366,12 @@ BootstrapArchCommon() { # pacman -T exits with 127 if there are missing dependencies missing=$($SUDO pacman -T $deps) || true + if [ "$ASSUME_YES" = 1 ]; then + noconfirm="--noconfirm" + fi + if [ "$missing" ]; then - $SUDO pacman -S --needed $missing + $SUDO pacman -S --needed $missing $noconfirm fi } @@ -426,7 +486,7 @@ Bootstrap() { elif grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon else - echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!" + echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo echo "You will need to bootstrap, configure virtualenv, and run pip install manually." echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites" @@ -446,7 +506,8 @@ if [ "$1" = "--le-auto-phase2" ]; then shift 1 # the --le-auto-phase2 arg if [ -f "$VENV_BIN/letsencrypt" ]; then # --version output ran through grep due to python-cryptography DeprecationWarnings - INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep ^letsencrypt | cut -d " " -f 2) + # grep for both certbot and letsencrypt until certbot and shim packages have been released + INSTALLED_VERSION=$("$VENV_BIN/letsencrypt" --version 2>&1 | grep "^certbot\|^letsencrypt" | cut -d " " -f 2) else INSTALLED_VERSION="none" fi @@ -465,8 +526,8 @@ if [ "$1" = "--le-auto-phase2" ]; then # There is no $ interpolation due to quotes on starting heredoc delimiter. # ------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/letsencrypt-auto-requirements.txt" -# This is the flattened list of packages letsencrypt-auto installs. To generate -# this, do `pip install --no-cache-dir -e acme -e . -e letsencrypt-apache`, and +# This is the flattened list of packages certbot-auto installs. To generate +# this, do `pip install --no-cache-dir -e acme -e . -e certbot-apache`, and # then use `hashin` or a more secure method to gather the hashes. argparse==1.4.0 \ @@ -645,15 +706,21 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.5.0 \ - --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ - --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd -letsencrypt==0.5.0 \ - --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ - --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a -letsencrypt-apache==0.5.0 \ - --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ - --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -823,18 +890,30 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run letsencrypt..." - echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" - $SUDO "$VENV_BIN/letsencrypt" "$@" + echo "Requesting root privileges to run certbot..." + if [ -z "$SUDO_ENV" ] ; then + # SUDO is su wrapper / noop + echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" + $SUDO "$VENV_BIN/letsencrypt" "$@" + else + # sudo + echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" + fi + else - # Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke. + # Phase 1: Upgrade certbot-auto if neceesary, then self-invoke. # # Each phase checks the version of only the thing it is responsible for # upgrading. Phase 1 checks the version of the latest release of - # letsencrypt-auto (which is always the same as that of the letsencrypt - # package). Phase 2 checks the version of the locally installed letsencrypt. + # certbot-auto (which is always the same as that of the certbot + # package). Phase 2 checks the version of the locally installed certbot. if [ ! -f "$VENV_BIN/letsencrypt" ]; then + if [ "$HELP" = 1 ]; then + echo "$USAGE" + exit 0 + fi # If it looks like we've never bootstrapped before, bootstrap: Bootstrap fi @@ -953,7 +1032,7 @@ def verified_new_le_auto(get, tag, temp_dir): stderr=dev_null) except CalledProcessError as exc: raise ExpectedError("Couldn't verify signature of downloaded " - "letsencrypt-auto.", exc) + "certbot-auto.", exc) def main(): @@ -978,29 +1057,27 @@ if __name__ == '__main__': UNLIKELY_EOF # --------------------------------------------------------------------------- DeterminePythonVersion - REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` - if [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then - echo "Upgrading letsencrypt-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." + if ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then + echo "WARNING: unable to check for updates." + elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + echo "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more # dependencies (curl, etc.), for better flow control, and for the option of # future Windows compatibility. "$LE_PYTHON" "$TEMP_DIR/fetch.py" --le-auto-script "v$REMOTE_VERSION" - # Install new copy of letsencrypt-auto. + # Install new copy of certbot-auto. # TODO: Deal with quotes in pathnames. - echo "Replacing letsencrypt-auto..." + echo "Replacing certbot-auto..." # Clone permissions with cp. chmod and chown don't have a --reference # option on OS X or BSD, and stat -c on Linux is stat -f on OS X and BSD: - echo " " $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp -p "$0" "$TEMP_DIR/letsencrypt-auto.permission-clone" - echo " " $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" $SUDO cp "$TEMP_DIR/letsencrypt-auto" "$TEMP_DIR/letsencrypt-auto.permission-clone" # Using mv rather than cp leaves the old file descriptor pointing to the # original copy so the shell can continue to read it unmolested. mv across # filesystems is non-atomic, doing `rm dest, cp src dest, rm src`, but the # cp is unlikely to fail (esp. under sudo) if the rm doesn't. - echo " " $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" $SUDO mv -f "$TEMP_DIR/letsencrypt-auto.permission-clone" "$0" # TODO: Clean up temp dir safely, even if it has quotes in its path. rm -rf "$TEMP_DIR" diff --git a/letsencrypt-auto-source/certbot-auto.asc b/letsencrypt-auto-source/certbot-auto.asc new file mode 100644 index 000000000..8b4f34c70 --- /dev/null +++ b/letsencrypt-auto-source/certbot-auto.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1 + +iQEcBAABAgAGBQJXM9ZDAAoJEE0XyZXNl3XyzGkH/2KeR0jYxXKlvwfCkxU6hSC0 +eXcxZVQk59hCSvkNGE6Mj6rwQcyjSqmRp14MaJpq7NZADN6F+HWb6VB/Wq6moMQs +PJtthqwhF767Qg+Py9Hp6XmlKscjXB6AKCVxq5TBwEIOTtj0rhQRLF9/+GW6jFuf +kT6aUcDWNjOyWWUtp9vOVprDtegrltp0/2DNitlvPu263pKC+7I3GyLTq4fKP4EE +auZSAhFry9SNR3Usf2wD3kzhvLSrT3h9Yh5oA04oaX9H6e86EHwt6RJJRHpg8s6b +e0CBIIuaRJEmdiMUWlV/gAfH6M2PbG1wtJdxc0ThNEoWAjTsopr61BoHJ3cpCy4= +=+e7/ +-----END PGP SIGNATURE----- diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 8578feef2..eb5561070 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -19,7 +19,7 @@ XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share} VENV_NAME="letsencrypt" VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"} VENV_BIN="$VENV_PATH/bin" -LE_AUTO_VERSION="0.6.0.dev0" +LE_AUTO_VERSION="0.7.0.dev0" BASENAME=$(basename $0) USAGE="Usage: $BASENAME [OPTIONS] A self-updating wrapper script for the Certbot ACME client. When run, updates @@ -38,17 +38,6 @@ Help for certbot itself cannot be provided until it is installed. All arguments are accepted and forwarded to the Certbot client when run." -while getopts ":hnv" arg; do - case $arg in - h) - HELP=1;; - n) - ASSUME_YES=1;; - v) - VERBOSE=1;; - esac -done - for arg in "$@" ; do case "$arg" in --debug) @@ -65,9 +54,26 @@ for arg in "$@" ; do ASSUME_YES=1;; --verbose) VERBOSE=1;; + -[!-]*) + while getopts ":hnv" short_arg $arg; do + case "$short_arg" in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac + done;; esac done +if [ $BASENAME = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + # certbot-auto needs root access to bootstrap OS dependencies, and # certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but @@ -107,12 +113,6 @@ else SUDO= fi -if [ $BASENAME = "letsencrypt-auto" ]; then - # letsencrypt-auto does not respect --help or --yes for backwards compatibility - ASSUME_YES=1 - HELP=0 -fi - ExperimentalBootstrap() { # Arguments: Platform name, bootstrap function name if [ "$DEBUG" = 1 ]; then @@ -435,7 +435,8 @@ BootstrapMac() { # Workaround for _dlopen not finding augeas on OS X if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then echo "Applying augeas workaround" - $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib + $SUDO mkdir -p /usr/local/lib/ + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ fi if ! hash pip 2>/dev/null; then @@ -451,6 +452,11 @@ BootstrapMac() { fi } +BootstrapSmartOS() { + pkgin update + pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv' +} + # Install required OS packages: Bootstrap() { @@ -483,8 +489,10 @@ Bootstrap() { ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd elif uname | grep -iq Darwin ; then ExperimentalBootstrap "Mac OS X" BootstrapMac - elif grep -iq "Amazon Linux" /etc/issue ; then + elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon + elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then + ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS else echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo @@ -706,15 +714,21 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.5.0 \ - --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ - --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd -letsencrypt==0.5.0 \ - --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ - --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a -letsencrypt-apache==0.5.0 \ - --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ - --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 UNLIKELY_EOF # ------------------------------------------------------------------------- @@ -884,14 +898,16 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run certbot..." + if [ -n "$SUDO" ]; then + # SUDO is su wrapper or sudo + echo "Requesting root privileges to run certbot..." + echo " $VENV_BIN/letsencrypt" "$@" + fi if [ -z "$SUDO_ENV" ] ; then # SUDO is su wrapper / noop - echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else # sudo - echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" fi @@ -917,7 +933,6 @@ else fi if [ "$NO_SELF_UPGRADE" != 1 ]; then - echo "Checking for new version..." TEMP_DIR=$(TempDir) # --------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" @@ -1010,7 +1025,7 @@ def verified_new_le_auto(get, tag, temp_dir): """ le_auto_dir = environ.get( 'LE_AUTO_DIR_TEMPLATE', - 'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/' + 'https://raw.githubusercontent.com/certbot/certbot/%s/' 'letsencrypt-auto-source/') % tag write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto') write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig') diff --git a/letsencrypt-auto-source/letsencrypt-auto.sig b/letsencrypt-auto-source/letsencrypt-auto.sig index 36ab206aa..cb360f89e 100644 Binary files a/letsencrypt-auto-source/letsencrypt-auto.sig and b/letsencrypt-auto-source/letsencrypt-auto.sig differ diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 33b140bca..f1ed82c4c 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -38,17 +38,6 @@ Help for certbot itself cannot be provided until it is installed. All arguments are accepted and forwarded to the Certbot client when run." -while getopts ":hnv" arg; do - case $arg in - h) - HELP=1;; - n) - ASSUME_YES=1;; - v) - VERBOSE=1;; - esac -done - for arg in "$@" ; do case "$arg" in --debug) @@ -65,9 +54,26 @@ for arg in "$@" ; do ASSUME_YES=1;; --verbose) VERBOSE=1;; + -[!-]*) + while getopts ":hnv" short_arg $arg; do + case "$short_arg" in + h) + HELP=1;; + n) + ASSUME_YES=1;; + v) + VERBOSE=1;; + esac + done;; esac done +if [ $BASENAME = "letsencrypt-auto" ]; then + # letsencrypt-auto does not respect --help or --yes for backwards compatibility + ASSUME_YES=1 + HELP=0 +fi + # certbot-auto needs root access to bootstrap OS dependencies, and # certbot itself needs root access for almost all modes of operation # The "normal" case is that sudo is used for the steps that need root, but @@ -107,12 +113,6 @@ else SUDO= fi -if [ $BASENAME = "letsencrypt-auto" ]; then - # letsencrypt-auto does not respect --help or --yes for backwards compatibility - ASSUME_YES=1 - HELP=0 -fi - ExperimentalBootstrap() { # Arguments: Platform name, bootstrap function name if [ "$DEBUG" = 1 ]; then @@ -154,6 +154,7 @@ DeterminePythonVersion() { {{ bootstrappers/gentoo_common.sh }} {{ bootstrappers/free_bsd.sh }} {{ bootstrappers/mac.sh }} +{{ bootstrappers/smartos.sh }} # Install required OS packages: Bootstrap() { @@ -186,8 +187,10 @@ Bootstrap() { ExperimentalBootstrap "FreeBSD" BootstrapFreeBsd elif uname | grep -iq Darwin ; then ExperimentalBootstrap "Mac OS X" BootstrapMac - elif grep -iq "Amazon Linux" /etc/issue ; then + elif [ -f /etc/issue ] && grep -iq "Amazon Linux" /etc/issue ; then ExperimentalBootstrap "Amazon Linux" BootstrapRpmCommon + elif [ -f /etc/product ] && grep -q "Joyent Instance" /etc/product ; then + ExperimentalBootstrap "Joyent SmartOS Zone" BootstrapSmartOS else echo "Sorry, I don't know how to bootstrap Certbot on your operating system!" echo @@ -252,14 +255,16 @@ UNLIKELY_EOF fi echo "Installation succeeded." fi - echo "Requesting root privileges to run certbot..." + if [ -n "$SUDO" ]; then + # SUDO is su wrapper or sudo + echo "Requesting root privileges to run certbot..." + echo " $VENV_BIN/letsencrypt" "$@" + fi if [ -z "$SUDO_ENV" ] ; then # SUDO is su wrapper / noop - echo " " $SUDO "$VENV_BIN/letsencrypt" "$@" $SUDO "$VENV_BIN/letsencrypt" "$@" else # sudo - echo " " $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" $SUDO "$SUDO_ENV" "$VENV_BIN/letsencrypt" "$@" fi @@ -285,7 +290,6 @@ else fi if [ "$NO_SELF_UPGRADE" != 1 ]; then - echo "Checking for new version..." TEMP_DIR=$(TempDir) # --------------------------------------------------------------------------- cat << "UNLIKELY_EOF" > "$TEMP_DIR/fetch.py" diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index 79e58eb3f..2b04977c8 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -16,7 +16,8 @@ BootstrapMac() { $pkgcmd augeas $pkgcmd dialog - if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" ]; then + if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" \ + -o "$(which python)" = "/usr/bin/python" ]; then # We want to avoid using the system Python because it requires root to use pip. # python.org, MacPorts or HomeBrew Python installations should all be OK. echo "Installing python..." @@ -26,7 +27,8 @@ BootstrapMac() { # Workaround for _dlopen not finding augeas on OS X if [ "$pkgman" = "port" ] && ! [ -e "/usr/local/lib/libaugeas.dylib" ] && [ -e "/opt/local/lib/libaugeas.dylib" ]; then echo "Applying augeas workaround" - $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib + $SUDO mkdir -p /usr/local/lib/ + $SUDO ln -s /opt/local/lib/libaugeas.dylib /usr/local/lib/ fi if ! hash pip 2>/dev/null; then diff --git a/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh b/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh new file mode 100644 index 000000000..e721c1c0b --- /dev/null +++ b/letsencrypt-auto-source/pieces/bootstrappers/smartos.sh @@ -0,0 +1,4 @@ +BootstrapSmartOS() { + pkgin update + pkgin -y install 'gcc49' 'py27-augeas' 'py27-virtualenv' +} diff --git a/letsencrypt-auto-source/pieces/fetch.py b/letsencrypt-auto-source/pieces/fetch.py index 38f4aa255..ca3e94b80 100644 --- a/letsencrypt-auto-source/pieces/fetch.py +++ b/letsencrypt-auto-source/pieces/fetch.py @@ -87,7 +87,7 @@ def verified_new_le_auto(get, tag, temp_dir): """ le_auto_dir = environ.get( 'LE_AUTO_DIR_TEMPLATE', - 'https://raw.githubusercontent.com/letsencrypt/letsencrypt/%s/' + 'https://raw.githubusercontent.com/certbot/certbot/%s/' 'letsencrypt-auto-source/') % tag write(get(le_auto_dir + 'letsencrypt-auto'), temp_dir, 'letsencrypt-auto') write(get(le_auto_dir + 'letsencrypt-auto.sig'), temp_dir, 'letsencrypt-auto.sig') diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index 3a1ce34f1..f4ceae536 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -178,12 +178,18 @@ mock==1.0.1 \ # THE LINES BELOW ARE EDITED BY THE RELEASE SCRIPT; ADD ALL DEPENDENCIES ABOVE. -acme==0.5.0 \ - --hash=sha256:ceb4127c13213f0006a564be82176b968c6b374d20d9fc78555d0658a252b275 \ - --hash=sha256:0605c63c656d33c883a05675f5db9cfb85d503f2771c885031800e0da7631abd -letsencrypt==0.5.0 \ - --hash=sha256:f90f883e99cdbdf8142335bdbf4f74a8af143ee4b4ec60fb49c6e47418c1114c \ - --hash=sha256:e38a2b70b82be79bc195307652244a3e012ec73d897d4dbd3f80cf698496d15a -letsencrypt-apache==0.5.0 \ - --hash=sha256:a767882164a7b09d9c12c80684a28a782135fdaf35654ef5a02c0b7b1d27ab8d \ - --hash=sha256:c20e7b9c517aa4a7d70e6bd9382da7259f00bc191b9e60d8e312e48837a00c41 +acme==0.6.0 \ + --hash=sha256:cbe4e7a340a19725a8740ed86e30abdbe18fc22c4c6022b7a8e56642d502bcc3 \ + --hash=sha256:ec4e6009dfbd629b58473eb06bbebfd9fb2a79fc8831c149e9205bc38a98ecc6 +certbot==0.6.0 \ + --hash=sha256:a893632d228864b0a751db9f3fdd93439ed34b988ea21b64fb0f0fa2ceded6a2 \ + --hash=sha256:80b0b7dc5afeec2816ef638a61e7c628d73cd72666eebf4984be426d1c2b492d +certbot-apache==0.6.0 \ + --hash=sha256:0ab077f0913b81ed5c1b141c3a7c4c0228ef3738d8d61a93db794d9a80718d43 \ + --hash=sha256:1cfbe751209079a803758f472200816fac559f2a36fdd582d25e3ba5601423a1 +letsencrypt==0.6.0 \ + --hash=sha256:93196c7dcd57272a753e525d145c5a9987c8968c22ec954bcf83dcc9d2499a76 \ + --hash=sha256:a16d6c395f1bf5fd61a28ef83dc78f42dbecbad9d00be6236f2ad8915645c154 +letsencrypt-apache==0.6.0 \ + --hash=sha256:02fadc52a0796e53978c508beec9c53e1fc047660240832b9bde5d53ab3a1379 \ + --hash=sha256:1c5522d94d7750bdb9bfa6201d2c263e914f662c9d0079e673167233cf4364f1 diff --git a/letsencrypt-nginx/setup.py b/letsencrypt-nginx/setup.py index b94b7f69f..95ffd6cd8 100644 --- a/letsencrypt-nginx/setup.py +++ b/letsencrypt-nginx/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0.dev0' +version = '0.7.0.dev0' # This package is a simple shim around certbot-nginx diff --git a/letsencrypt/setup.py b/letsencrypt/setup.py index 708c31f4b..7c974ea9b 100644 --- a/letsencrypt/setup.py +++ b/letsencrypt/setup.py @@ -20,7 +20,7 @@ readme = read_file(os.path.join(here, 'README.rst')) install_requires = ['certbot'] -version = '0.6.0.dev0' +version = '0.7.0.dev0' setup( diff --git a/letshelp-certbot/docs/conf.py b/letshelp-certbot/docs/conf.py index 905d70662..17d8b3ea9 100644 --- a/letshelp-certbot/docs/conf.py +++ b/letshelp-certbot/docs/conf.py @@ -307,5 +307,5 @@ texinfo_documents = [ intersphinx_mapping = { 'python': ('https://docs.python.org/', None), 'acme': ('https://acme-python.readthedocs.org/en/latest/', None), - 'certbot': ('https://letsencrypt.readthedocs.org/en/latest/', None), + 'certbot': ('https://certbot.eff.org/docs/', None), } diff --git a/letshelp-certbot/setup.py b/letshelp-certbot/setup.py index 8359d2766..b616da688 100644 --- a/letshelp-certbot/setup.py +++ b/letshelp-certbot/setup.py @@ -4,7 +4,7 @@ from setuptools import setup from setuptools import find_packages -version = '0.6.0.dev0' +version = '0.7.0.dev0' install_requires = [ 'setuptools', # pkg_resources diff --git a/letshelp-letsencrypt/setup.py b/letshelp-letsencrypt/setup.py index 875c6fc92..10380c49b 100644 --- a/letshelp-letsencrypt/setup.py +++ b/letshelp-letsencrypt/setup.py @@ -16,7 +16,7 @@ here = os.path.abspath(os.path.dirname(__file__)) readme = read_file(os.path.join(here, 'README.rst')) -version = '0.6.0.dev0' +version = '0.7.0.dev0' # This package is a simple shim around letshelp-certbot diff --git a/tools/release.sh b/tools/release.sh index e6169c5c8..abaad09ff 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -145,6 +145,9 @@ pip install \ kill $! cd ~- +# get a snapshot of the CLI help for the docs +certbot --help all > docs/cli-help.txt + # freeze before installing anything else, so that we know end-user KGS # make sure "twine upload" doesn't catch "kgs" if [ -d ../kgs ] ; then @@ -187,11 +190,17 @@ while ! openssl dgst -sha256 -verify $RELEASE_OPENSSL_PUBKEY -signature \ read -p "Please correctly sign letsencrypt-auto with offline-signrequest.sh" done +# This signature is not quite as strong, but easier for people to verify out of band +gpg -u "$RELEASE_GPG_KEY" --detach-sign --armor --sign letsencrypt-auto-source/letsencrypt-auto +# We can't rename the openssl letsencrypt-auto.sig for compatibility reasons, +# but we can use the right name for cerbot-auto.asc from day one +mv letsencrypt-auto-source/letsencrypt-auto.asc letsencrypt-auto-source/certbot-auto.asc + # copy leauto to the root, overwriting the previous release version cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto -git add certbot-auto letsencrypt-auto letsencrypt-auto-source +git add certbot-auto letsencrypt-auto letsencrypt-auto-source docs/cli-help.txt git diff --cached git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version" git tag --local-user "$RELEASE_GPG_KEY" --sign --message "Release $version" "$tag"