mirror of
https://github.com/certbot/certbot.git
synced 2026-07-28 00:35:50 +02:00
Merge remote-tracking branch 'origin/master' into webroot
This commit is contained in:
@@ -136,7 +136,7 @@ class Addr(object):
|
||||
|
||||
|
||||
class TLSSNI01(object):
|
||||
"""Class that performs tls-sni-01 challenges."""
|
||||
"""Abstract base for TLS-SNI-01 challenge performers"""
|
||||
|
||||
def __init__(self, configurator):
|
||||
self.configurator = configurator
|
||||
|
||||
@@ -46,8 +46,6 @@ Make sure your web server displays the following content at
|
||||
|
||||
{validation}
|
||||
|
||||
Content-Type header MUST be set to {ct}.
|
||||
|
||||
If you don't have HTTP server configured, you can run the following
|
||||
command on the target server (as root):
|
||||
|
||||
@@ -75,7 +73,6 @@ printf "%s" {validation} > {achall.URI_ROOT_PATH}/{encoded_token}
|
||||
# run only once per server:
|
||||
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \\
|
||||
"import BaseHTTPServer, SimpleHTTPServer; \\
|
||||
SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map = {{'': '{ct}'}}; \\
|
||||
s = BaseHTTPServer.HTTPServer(('', {port}), SimpleHTTPServer.SimpleHTTPRequestHandler); \\
|
||||
s.serve_forever()" """
|
||||
"""Command template."""
|
||||
@@ -90,6 +87,8 @@ s.serve_forever()" """
|
||||
def add_parser_arguments(cls, add):
|
||||
add("test-mode", action="store_true",
|
||||
help="Test mode. Executes the manual command in subprocess.")
|
||||
add("public-ip-logging-ok", action="store_true",
|
||||
help="Automatically allows public IP logging.")
|
||||
|
||||
def prepare(self): # pylint: disable=missing-docstring,no-self-use
|
||||
pass # pragma: no cover
|
||||
@@ -140,7 +139,7 @@ s.serve_forever()" """
|
||||
# TODO(kuba): pipes still necessary?
|
||||
validation=pipes.quote(validation),
|
||||
encoded_token=achall.chall.encode("token"),
|
||||
ct=achall.CONTENT_TYPE, port=port)
|
||||
port=port)
|
||||
if self.conf("test-mode"):
|
||||
logger.debug("Test mode. Executing the manual command: %s", command)
|
||||
# sh shipped with OS X does't support echo -n, but supports printf
|
||||
@@ -164,26 +163,22 @@ s.serve_forever()" """
|
||||
if self._httpd.poll() is not None:
|
||||
raise errors.Error("Couldn't execute manual command")
|
||||
else:
|
||||
if not zope.component.getUtility(interfaces.IDisplay).yesno(
|
||||
self.IP_DISCLAIMER, "Yes", "No"):
|
||||
raise errors.PluginError("Must agree to IP logging to proceed")
|
||||
if not self.conf("public-ip-logging-ok"):
|
||||
if not zope.component.getUtility(interfaces.IDisplay).yesno(
|
||||
self.IP_DISCLAIMER, "Yes", "No"):
|
||||
raise errors.PluginError("Must agree to IP logging to proceed")
|
||||
|
||||
self._notify_and_wait(self.MESSAGE_TEMPLATE.format(
|
||||
validation=validation, response=response,
|
||||
uri=achall.chall.uri(achall.domain),
|
||||
ct=achall.CONTENT_TYPE, command=command))
|
||||
command=command))
|
||||
|
||||
if response.simple_verify(
|
||||
if not response.simple_verify(
|
||||
achall.chall, achall.domain,
|
||||
achall.account_key.public_key(), self.config.http01_port):
|
||||
return response
|
||||
else:
|
||||
logger.error(
|
||||
"Self-verify of challenge failed, authorization abandoned.")
|
||||
if self.conf("test-mode") and self._httpd.poll() is not None:
|
||||
# simply verify cause command failure...
|
||||
return False
|
||||
return None
|
||||
logger.warning("Self-verify of challenge failed.")
|
||||
|
||||
return response
|
||||
|
||||
def _notify_and_wait(self, message): # pylint: disable=no-self-use
|
||||
# TODO: IDisplay wraps messages, breaking the command
|
||||
|
||||
@@ -23,7 +23,7 @@ class AuthenticatorTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from letsencrypt.plugins.manual import Authenticator
|
||||
self.config = mock.MagicMock(
|
||||
http01_port=8080, manual_test_mode=False)
|
||||
http01_port=8080, manual_test_mode=False, manual_public_ip_logging_ok=False)
|
||||
self.auth = Authenticator(config=self.config, name="manual")
|
||||
self.achalls = [achallenges.KeyAuthorizationAnnotatedChallenge(
|
||||
challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY)]
|
||||
@@ -61,7 +61,9 @@ class AuthenticatorTest(unittest.TestCase):
|
||||
self.assertTrue(self.achalls[0].chall.encode("token") in message)
|
||||
|
||||
mock_verify.return_value = False
|
||||
self.assertEqual([None], self.auth.perform(self.achalls))
|
||||
with mock.patch("letsencrypt.plugins.manual.logger") as mock_logger:
|
||||
self.auth.perform(self.achalls)
|
||||
mock_logger.warning.assert_called_once_with(mock.ANY)
|
||||
|
||||
@mock.patch("letsencrypt.plugins.manual.zope.component.getUtility")
|
||||
@mock.patch("letsencrypt.plugins.manual.Authenticator._notify_and_wait")
|
||||
@@ -87,20 +89,6 @@ class AuthenticatorTest(unittest.TestCase):
|
||||
self.assertRaises(
|
||||
errors.Error, self.auth_test_mode.perform, self.achalls)
|
||||
|
||||
@mock.patch("letsencrypt.plugins.manual.socket.socket")
|
||||
@mock.patch("letsencrypt.plugins.manual.time.sleep", autospec=True)
|
||||
@mock.patch("acme.challenges.HTTP01Response.simple_verify",
|
||||
autospec=True)
|
||||
@mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True)
|
||||
def test_perform_test_mode(self, mock_popen, mock_verify, mock_sleep,
|
||||
mock_socket):
|
||||
mock_popen.return_value.poll.side_effect = [None, 10]
|
||||
mock_popen.return_value.pid = 1234
|
||||
mock_verify.return_value = False
|
||||
self.assertEqual([False], self.auth_test_mode.perform(self.achalls))
|
||||
self.assertEqual(1, mock_sleep.call_count)
|
||||
self.assertEqual(1, mock_socket.call_count)
|
||||
|
||||
def test_cleanup_test_mode_already_terminated(self):
|
||||
# pylint: disable=protected-access
|
||||
self.auth_test_mode._httpd = httpd = mock.Mock()
|
||||
|
||||
@@ -1,43 +1,4 @@
|
||||
"""Webroot plugin.
|
||||
|
||||
Content-Type
|
||||
------------
|
||||
|
||||
This plugin requires your webserver to use a specific `Content-Type`
|
||||
header in the HTTP response.
|
||||
|
||||
Apache2
|
||||
~~~~~~~
|
||||
|
||||
.. note:: Instructions written and tested for Debian Jessie. Other
|
||||
operating systems might use something very similar, but you might
|
||||
still need to readjust some commands.
|
||||
|
||||
Create ``/etc/apache2/conf-available/letsencrypt.conf``, with
|
||||
the following contents::
|
||||
|
||||
<IfModule mod_headers.c>
|
||||
<LocationMatch "/.well-known/acme-challenge/*">
|
||||
Header set Content-Type "text/plain"
|
||||
</LocationMatch>
|
||||
</IfModule>
|
||||
|
||||
and then run ``a2enmod headers; a2enconf letsencrypt``; depending on the
|
||||
output you will have to either ``service apache2 restart`` or ``service
|
||||
apache2 reload``.
|
||||
|
||||
nginx
|
||||
~~~~~
|
||||
|
||||
Use the following snippet in your ``server{...}`` stanza::
|
||||
|
||||
location ~ /.well-known/acme-challenge/(.*) {
|
||||
default_type text/plain;
|
||||
}
|
||||
|
||||
and reload your daemon.
|
||||
|
||||
"""
|
||||
"""Webroot plugin."""
|
||||
import errno
|
||||
import logging
|
||||
import os
|
||||
|
||||
Reference in New Issue
Block a user