IConfig.simple_http_port (fixes #542).

This commit is contained in:
Jakub Warmuz 2015-06-24 15:48:27 +00:00
parent 29e56d442f
commit 2ec451d00b
No known key found for this signature in database
GPG key ID: 2A7BAD3A489B52EA
6 changed files with 41 additions and 8 deletions

View file

@ -88,6 +88,11 @@ class SimpleHTTPResponse(ChallengeResponse):
"""URL scheme for the provisioned resource."""
return "https" if self.tls else "http"
@property
def port(self):
"""Port that the ACME client should be listening for validation."""
return 443 if self.tls else 80
def uri(self, domain):
"""Create an URI to the provisioned resource.
@ -100,7 +105,7 @@ class SimpleHTTPResponse(ChallengeResponse):
return self._URI_TEMPLATE.format(
scheme=self.scheme, domain=domain, path=self.path)
def simple_verify(self, chall, domain):
def simple_verify(self, chall, domain, port=None):
"""Simple verify.
According to the ACME specification, "the ACME server MUST
@ -109,12 +114,21 @@ class SimpleHTTPResponse(ChallengeResponse):
:param .SimpleHTTP chall: Corresponding challenge.
:param str domain: Domain name being verified.
:param int port: Port used in the validation.
:returns: ``True`` iff validation is successful, ``False``
otherwise.
:rtype: bool
"""
# TODO: ACME specification defines URI template that doesn't
# allow to use a custom port... Make sure port is not in the
# request URI, if it's standard.
if port is not None and port != self.port:
logger.warn(
"Using non-standard port for SimpleHTTP verification: %s", port)
domain += ":{0}".format(port)
uri = self.uri(domain)
logger.debug("Verifying %s at %s...", chall.typ, uri)
try:

View file

@ -7,6 +7,7 @@ import Crypto.PublicKey.RSA
import M2Crypto
import mock
import requests
import urlparse
from acme import jose
from acme import other
@ -85,6 +86,10 @@ class SimpleHTTPResponseTest(unittest.TestCase):
self.assertEqual('http', self.msg_http.scheme)
self.assertEqual('https', self.msg_https.scheme)
def test_port(self):
self.assertEqual(80, self.msg_http.port)
self.assertEqual(443, self.msg_https.port)
def test_uri(self):
self.assertEqual(
'http://example.com/.well-known/acme-challenge/'
@ -134,6 +139,12 @@ class SimpleHTTPResponseTest(unittest.TestCase):
mock_get.side_effect = requests.exceptions.RequestException
self.assertFalse(self.resp_http.simple_verify(self.chall, "local"))
@mock.patch("acme.challenges.requests.get")
def test_simple_verify_port(self, mock_get):
self.resp_http.simple_verify(self.chall, "local", 4430)
self.assertEqual("local:4430", urlparse.urlparse(
mock_get.mock_calls[0][1][0]).netloc)
class DVSNITest(unittest.TestCase):

View file

@ -480,9 +480,11 @@ def create_parser(plugins, args):
"testing", "--no-verify-ssl", action="store_true",
help=config_help("no_verify_ssl"),
default=flag_default("no_verify_ssl"))
helpful.add( # TODO: apache and nginx plugins do NOT respect it (#479)
helpful.add( # TODO: apache and nginx plugins do NOT respect it (#479)
"testing", "--dvsni-port", type=int, default=flag_default("dvsni_port"),
help=config_help("dvsni_port"))
helpful.add("testing", "--simple-http-port", type=int,
help=config_help("simple_http_port"))
helpful.add("testing", "--no-simple-http-tls", action="store_true",
help=config_help("no_simple_http_tls"))

View file

@ -188,6 +188,8 @@ class IConfig(zope.interface.Interface):
no_simple_http_tls = zope.interface.Attribute(
"Do not use TLS when solving SimpleHTTP challenges.")
simple_http_port = zope.interface.Attribute(
"Port used in the SimpleHttp challenge.")
class IInstaller(IPlugin):

View file

@ -49,7 +49,7 @@ echo -n {achall.token} > {response.URI_ROOT_PATH}/{response.path}
# run only once per server:
python -c "import BaseHTTPServer, SimpleHTTPServer; \\
SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map = {{'': '{ct}'}}; \\
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \\
s = BaseHTTPServer.HTTPServer(('', {port}), SimpleHTTPServer.SimpleHTTPRequestHandler); \\
s.serve_forever()" """
"""Non-TLS command template."""
@ -62,7 +62,7 @@ echo -n {achall.token} > {response.URI_ROOT_PATH}/{response.path}
openssl req -new -newkey rsa:4096 -subj "/" -days 1 -nodes -x509 -keyout ../key.pem -out ../cert.pem
python -c "import BaseHTTPServer, SimpleHTTPServer, ssl; \\
SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map = {{'': '{ct}'}}; \\
s = BaseHTTPServer.HTTPServer(('', 443), SimpleHTTPServer.SimpleHTTPRequestHandler); \\
s = BaseHTTPServer.HTTPServer(('', {port}), SimpleHTTPServer.SimpleHTTPRequestHandler); \\
s.socket = ssl.wrap_socket(s.socket, keyfile='../key.pem', certfile='../cert.pem'); \\
s.serve_forever()" """
"""TLS command template.
@ -113,9 +113,12 @@ binary for temporary key/certificate generation.""".replace("\n", "")
self._notify_and_wait(self.MESSAGE_TEMPLATE.format(
achall=achall, response=response, uri=response.uri(achall.domain),
ct=response.CONTENT_TYPE, command=self.template.format(
achall=achall, response=response, ct=response.CONTENT_TYPE)))
achall=achall, response=response, ct=response.CONTENT_TYPE,
port=(response.port if self.config.simple_http_port is None
else self.config.simple_http_port))))
if response.simple_verify(achall.challb, achall.domain):
if response.simple_verify(
achall.challb, achall.domain, self.config.simple_http_port):
return response
else:
return None

View file

@ -14,7 +14,8 @@ class ManualAuthenticatorTest(unittest.TestCase):
def setUp(self):
from letsencrypt.plugins.manual import ManualAuthenticator
self.config = mock.MagicMock(no_simple_http_tls=True)
self.config = mock.MagicMock(
no_simple_http_tls=True, simple_http_port=4430)
self.auth = ManualAuthenticator(config=self.config, name="manual")
self.achalls = [achallenges.SimpleHTTP(
challb=acme_util.SIMPLE_HTTP, domain="foo.com", key=None)]
@ -41,7 +42,7 @@ class ManualAuthenticatorTest(unittest.TestCase):
resp = challenges.SimpleHTTPResponse(tls=False, path='Zm9v')
self.assertEqual([resp], self.auth.perform(self.achalls))
mock_raw_input.assert_called_once()
mock_verify.assert_called_with(self.achalls[0].challb, "foo.com")
mock_verify.assert_called_with(self.achalls[0].challb, "foo.com", 4430)
message = mock_stdout.write.mock_calls[0][1][0]
self.assertTrue(self.achalls[0].token in message)