http-01 for manual plugin

This commit is contained in:
Jakub Warmuz
2015-11-01 11:01:22 +00:00
parent 5ef2507b0c
commit fd209eab66
2 changed files with 17 additions and 17 deletions
+12 -12
View File
@@ -27,7 +27,7 @@ class Authenticator(common.Plugin):
"""Manual Authenticator. """Manual Authenticator.
This plugin requires user's manual intervention in setting up a HTTP This plugin requires user's manual intervention in setting up a HTTP
server for solving SimpleHTTP challenges and thus does not need to be server for solving http-01 challenges and thus does not need to be
run as a privileged process. Alternatively shows instructions on how run as a privileged process. Alternatively shows instructions on how
to use Python's built-in HTTP server. to use Python's built-in HTTP server.
@@ -68,9 +68,9 @@ Are you OK with your IP being logged?
# anything recursively under the cwd # anything recursively under the cwd
CMD_TEMPLATE = """\ CMD_TEMPLATE = """\
mkdir -p {root}/public_html/{response.URI_ROOT_PATH} mkdir -p {root}/public_html/{achall.URI_ROOT_PATH}
cd {root}/public_html cd {root}/public_html
printf "%s" {validation} > {response.URI_ROOT_PATH}/{encoded_token} printf "%s" {validation} > {achall.URI_ROOT_PATH}/{encoded_token}
# run only once per server: # run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \\ $(command -v python2 || command -v python2.7 || command -v python2.6) -c \\
"import BaseHTTPServer, SimpleHTTPServer; \\ "import BaseHTTPServer, SimpleHTTPServer; \\
@@ -95,14 +95,14 @@ s.serve_forever()" """
def more_info(self): # pylint: disable=missing-docstring,no-self-use def more_info(self): # pylint: disable=missing-docstring,no-self-use
return ("This plugin requires user's manual intervention in setting " return ("This plugin requires user's manual intervention in setting "
"up an HTTP server for solving SimpleHTTP challenges and thus " "up an HTTP server for solving http-01 challenges and thus "
"does not need to be run as a privileged process. " "does not need to be run as a privileged process. "
"Alternatively shows instructions on how to use Python's " "Alternatively shows instructions on how to use Python's "
"built-in HTTP server.") "built-in HTTP server.")
def get_chall_pref(self, domain): def get_chall_pref(self, domain):
# pylint: disable=missing-docstring,no-self-use,unused-argument # pylint: disable=missing-docstring,no-self-use,unused-argument
return [challenges.SimpleHTTP] return [challenges.HTTP01]
def perform(self, achalls): # pylint: disable=missing-docstring def perform(self, achalls): # pylint: disable=missing-docstring
responses = [] responses = []
@@ -130,16 +130,16 @@ s.serve_forever()" """
# same path for each challenge response would be easier for # same path for each challenge response would be easier for
# users, but will not work if multiple domains point at the # users, but will not work if multiple domains point at the
# same server: default command doesn't support virtual hosts # same server: default command doesn't support virtual hosts
response, validation = achall.gen_response_and_validation( response, validation = achall.response_and_validation()
tls=False) # SimpleHTTP TLS is dead: ietf-wg-acme/acme#7
port = (response.port if self.config.simple_http_port is None port = (response.port if self.config.simple_http_port is None
else int(self.config.simple_http_port)) else int(self.config.simple_http_port))
command = self.CMD_TEMPLATE.format( command = self.CMD_TEMPLATE.format(
root=self._root, achall=achall, response=response, root=self._root, achall=achall, response=response,
validation=pipes.quote(validation.json_dumps()), # TODO(kuba): pipes still necessary?
validation=pipes.quote(validation),
encoded_token=achall.chall.encode("token"), encoded_token=achall.chall.encode("token"),
ct=response.CONTENT_TYPE, port=port) ct=achall.CONTENT_TYPE, port=port)
if self.conf("test-mode"): if self.conf("test-mode"):
logger.debug("Test mode. Executing the manual command: %s", command) logger.debug("Test mode. Executing the manual command: %s", command)
# sh shipped with OS X does't support echo -n, but supports printf # sh shipped with OS X does't support echo -n, but supports printf
@@ -168,9 +168,9 @@ s.serve_forever()" """
raise errors.PluginError("Must agree to IP logging to proceed") raise errors.PluginError("Must agree to IP logging to proceed")
self._notify_and_wait(self.MESSAGE_TEMPLATE.format( self._notify_and_wait(self.MESSAGE_TEMPLATE.format(
validation=validation.json_dumps(), response=response, validation=validation, response=response,
uri=response.uri(achall.domain, achall.challb.chall), uri=achall.chall.uri(achall.domain),
ct=response.CONTENT_TYPE, command=command)) ct=achall.CONTENT_TYPE, command=command))
if response.simple_verify( if response.simple_verify(
achall.chall, achall.domain, achall.chall, achall.domain,
+5 -5
View File
@@ -25,8 +25,8 @@ class AuthenticatorTest(unittest.TestCase):
self.config = mock.MagicMock( self.config = mock.MagicMock(
simple_http_port=8080, manual_test_mode=False) simple_http_port=8080, manual_test_mode=False)
self.auth = Authenticator(config=self.config, name="manual") self.auth = Authenticator(config=self.config, name="manual")
self.achalls = [achallenges.SimpleHTTP( self.achalls = [achallenges.KeyAuthorizationAnnotatedChallenge(
challb=acme_util.SIMPLE_HTTP_P, domain="foo.com", account_key=KEY)] challb=acme_util.HTTP01_P, domain="foo.com", account_key=KEY)]
config_test_mode = mock.MagicMock( config_test_mode = mock.MagicMock(
simple_http_port=8080, manual_test_mode=True) simple_http_port=8080, manual_test_mode=True)
@@ -45,13 +45,13 @@ class AuthenticatorTest(unittest.TestCase):
@mock.patch("letsencrypt.plugins.manual.zope.component.getUtility") @mock.patch("letsencrypt.plugins.manual.zope.component.getUtility")
@mock.patch("letsencrypt.plugins.manual.sys.stdout") @mock.patch("letsencrypt.plugins.manual.sys.stdout")
@mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify") @mock.patch("acme.challenges.HTTP01Response.simple_verify")
@mock.patch("__builtin__.raw_input") @mock.patch("__builtin__.raw_input")
def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction): def test_perform(self, mock_raw_input, mock_verify, mock_stdout, mock_interaction):
mock_verify.return_value = True mock_verify.return_value = True
mock_interaction().yesno.return_value = True mock_interaction().yesno.return_value = True
resp = challenges.SimpleHTTPResponse(tls=False) resp = self.achalls[0].response(KEY)
self.assertEqual([resp], self.auth.perform(self.achalls)) self.assertEqual([resp], self.auth.perform(self.achalls))
self.assertEqual(1, mock_raw_input.call_count) self.assertEqual(1, mock_raw_input.call_count)
mock_verify.assert_called_with( mock_verify.assert_called_with(
@@ -89,7 +89,7 @@ class AuthenticatorTest(unittest.TestCase):
@mock.patch("letsencrypt.plugins.manual.socket.socket") @mock.patch("letsencrypt.plugins.manual.socket.socket")
@mock.patch("letsencrypt.plugins.manual.time.sleep", autospec=True) @mock.patch("letsencrypt.plugins.manual.time.sleep", autospec=True)
@mock.patch("acme.challenges.SimpleHTTPResponse.simple_verify", @mock.patch("acme.challenges.HTTP01Response.simple_verify",
autospec=True) autospec=True)
@mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True) @mock.patch("letsencrypt.plugins.manual.subprocess.Popen", autospec=True)
def test_perform_test_mode(self, mock_popen, mock_verify, mock_sleep, def test_perform_test_mode(self, mock_popen, mock_verify, mock_sleep,