def _perform_single(self, achall): # same path for each challenge response would be easier for # users, but will not work if multiple domains point at the # same server: default command doesn't support virtual hosts response = challenges.SimpleHTTPResponse( path=jose.b64encode(os.urandom(18)), tls=(not self.config.no_simple_http_tls)) assert response.good_path # is encoded os.urandom(18) good? 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, 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, self.config.simple_http_port): return response else: return None
def _test_simple_http(self, add): chall = challenges.SimpleHTTP(token=(b'x' * 16)) response = challenges.SimpleHTTPResponse(tls=False) from acme.standalone import SimpleHTTPRequestHandler resource = SimpleHTTPRequestHandler.SimpleHTTPResource( chall=chall, response=response, validation=response.gen_validation( chall, self.account_key)) if add: self.resources.add(resource) return resource.response.simple_verify( resource.chall, 'localhost', self.account_key.public_key(), port=self.port)
def setUp(self): self.responses = ( challenges.SimpleHTTPResponse(path='Hf5GrX4Q7EBax9hc2jJnfw'), None, # null challenges.RecoveryTokenResponse(token='23029d88d9e123e'), ) self.contact = ("mailto:[email protected]", "tel:+12025551212") signature = other.Signature( alg=jose.RS256, jwk=jose.JWKRSA(key=KEY.publickey()), sig='-v\xd8\xc2\xa3\xba0\xd6\x92\x16\xb5.\xbe\xa1[\x04\xbe' '\x1b\xa1X\xd2)\x18\x94\x8f\xd7\xd0\xc0\xbbcI`W\xdf v' '\xe4\xed\xe8\x03J\xe8\xc8<?\xc8W\x94\x94cj(\xe7\xaa$' '\x92\xe9\x96\x11\xc2\xefx\x0bR', nonce='\xab?\x08o\xe6\x81$\x9f\xa1\xc9\x025\x1c\x1b\xa5+') from acme.messages import AuthorizationRequest self.msg = AuthorizationRequest( session_id='aefoGaavieG9Wihuk2aufai3aeZ5EeW4', nonce='\xec\xd6\xf2oYH\xeb\x13\xd5#q\xe0\xdd\xa2\x92\xa9', responses=self.responses, signature=signature, contact=self.contact, ) self.jmsg_to = { 'type': 'authorizationRequest', 'sessionID': 'aefoGaavieG9Wihuk2aufai3aeZ5EeW4', 'nonce': '7Nbyb1lI6xPVI3Hg3aKSqQ', 'responses': self.responses, 'signature': signature, 'contact': self.contact, } self.jmsg_from = { 'type': 'authorizationRequest', 'sessionID': 'aefoGaavieG9Wihuk2aufai3aeZ5EeW4', 'nonce': '7Nbyb1lI6xPVI3Hg3aKSqQ', 'responses': [ None if response is None else response.to_json() for response in self.responses ], 'signature': signature.to_json(), # TODO: schema validation doesn't recognize tuples as # arrays :( 'contact': list(self.contact), }
def test_perform(self, mock_raw_input, mock_verify, mock_stdout): mock_verify.return_value = True resp = challenges.SimpleHTTPResponse(tls=False) self.assertEqual([resp], self.auth.perform(self.achalls)) self.assertEqual(1, mock_raw_input.call_count) mock_verify.assert_called_with(self.achalls[0].challb.chall, "foo.com", KEY.public_key(), 4430) message = mock_stdout.write.mock_calls[0][1][0] self.assertTrue(self.achalls[0].chall.encode("token") in message) mock_verify.return_value = False self.assertEqual([None], self.auth.perform(self.achalls))
def _perform_single(self, achall): # same path for each challenge response would be easier for # users, but will not work if multiple domains point at the # same server: default command doesn't support virtual hosts response = challenges.SimpleHTTPResponse( path=jose.b64encode(os.urandom(18)), tls=(not self.config.no_simple_http_tls)) assert response.good_path # is encoded os.urandom(18) good? command = self.template.format( root=self._root, 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 self.conf("test-mode"): logger.debug("Test mode. Executing the manual command: %s", command) try: self._httpd = subprocess.Popen( command, # don't care about setting stdout and stderr, # we're in test mode anyway shell=True, # "preexec_fn" is UNIX specific, but so is "command" preexec_fn=os.setsid) except OSError as error: # ValueError should not happen! logger.debug( "Couldn't execute manual command: %s", error, exc_info=True) return False logger.debug("Manual command running as PID %s.", self._httpd.pid) # give it some time to bootstrap, before we try to verify # (cert generation in case of simpleHttpS might take time) time.sleep(4) # XXX if self._httpd.poll() is not None: raise errors.Error("Couldn't execute manual command") else: self._notify_and_wait(self.MESSAGE_TEMPLATE.format( achall=achall, response=response, uri=response.uri(achall.domain), ct=response.CONTENT_TYPE, command=command)) if response.simple_verify( achall.challb, achall.domain, self.config.simple_http_port): return response else: if self.conf("test-mode") and self._httpd.poll() is not None: # simply verify cause command failure... return False return None
def test_perform(self, mock_raw_input, mock_verify, mock_urandom, mock_stdout): mock_urandom.return_value = "foo" mock_verify.return_value = True 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", 4430) message = mock_stdout.write.mock_calls[0][1][0] self.assertTrue(self.achalls[0].token in message) self.assertTrue('Zm9v' in message) mock_verify.return_value = False self.assertEqual([None], self.auth.perform(self.achalls))
def gen_response_and_validation(self, tls): """Generates a SimpleHTTP response and validation. :param bool tls: True if TLS should be used :returns: ``(response, validation)`` tuple, where ``response`` is an instance of `acme.challenges.SimpleHTTPResponse` and ``validation`` is an instance of `acme.challenges.SimpleHTTPProvisionedResource`. :rtype: tuple """ response = challenges.SimpleHTTPResponse(tls=tls) validation = response.gen_validation(self.challb.chall, self.account_key) logger.debug("Simple HTTP validation payload: %s", validation.payload) return response, validation
def test_perform(self, mock_raw_input, mock_get, mock_urandom, mock_stdout): mock_urandom.return_value = "foo" mock_get().text = self.achalls[0].token self.assertEqual( [challenges.SimpleHTTPResponse(tls=False, path='Zm9v')], self.auth.perform(self.achalls)) mock_raw_input.assert_called_once() mock_get.assert_called_with( "http://foo.com/.well-known/acme-challenge/Zm9v", verify=False) message = mock_stdout.write.mock_calls[0][1][0] self.assertTrue(self.achalls[0].token in message) self.assertTrue('Zm9v' in message) mock_get().text = self.achalls[0].token + '!' self.assertEqual([None], self.auth.perform(self.achalls)) mock_get.side_effect = requests.exceptions.ConnectionError self.assertEqual([None], self.auth.perform(self.achalls))
def test_perform(self, mock_raw_input, mock_verify, mock_urandom, mock_stdout): mock_urandom.side_effect = nonrandom_urandom mock_verify.return_value = True resp = challenges.SimpleHTTPResponse(tls=False) self.assertEqual([resp], self.auth.perform(self.achalls)) self.assertEqual(1, mock_raw_input.call_count) mock_verify.assert_called_with(self.achalls[0].challb.chall, "foo.com", KEY.public_key(), 4430) message = mock_stdout.write.mock_calls[0][1][0] self.assertEqual( message, """\ Make sure your web server displays the following content at http://foo.com/.well-known/acme-challenge/ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ before continuing: {"header": {"alg": "RS256", "jwk": {"e": "AQAB", "kty": "RSA", "n": "rHVztFHtH92ucFJD_N_HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3C5Q"}}, "payload": "eyJ0bHMiOiBmYWxzZSwgInRva2VuIjogIlpYWmhSM2htUVVSek5uQlRVbUl5VEVGMk9VbGFaakUzUkhRemFuVjRSMG9yVUVOME9USjNjaXR2UVEiLCAidHlwZSI6ICJzaW1wbGVIdHRwIn0", "signature": "jFPJFC-2eRyBw7Sl0wyEBhsdvRZtKk8hc6HykEPAiofZlIwdIu76u2xHqMVZWSZdpxwMNUnnawTEAqgMWFydMA"} Content-Type header MUST be set to application/jose+json. If you don\'t have HTTP server configured, you can run the following command on the target server (as root): mkdir -p /tmp/letsencrypt/public_html/.well-known/acme-challenge cd /tmp/letsencrypt/public_html echo -n \'{"header": {"alg": "RS256", "jwk": {"e": "AQAB", "kty": "RSA", "n": "rHVztFHtH92ucFJD_N_HW9AsdRsUuHUBBBDlHwNlRd3fp580rv2-6QWE30cWgdmJS86ObRz6lUTor4R0T-3C5Q"}}, "payload": "eyJ0bHMiOiBmYWxzZSwgInRva2VuIjogIlpYWmhSM2htUVVSek5uQlRVbUl5VEVGMk9VbGFaakUzUkhRemFuVjRSMG9yVUVOME9USjNjaXR2UVEiLCAidHlwZSI6ICJzaW1wbGVIdHRwIn0", "signature": "jFPJFC-2eRyBw7Sl0wyEBhsdvRZtKk8hc6HykEPAiofZlIwdIu76u2xHqMVZWSZdpxwMNUnnawTEAqgMWFydMA"}\' > .well-known/acme-challenge/ZXZhR3hmQURzNnBTUmIyTEF2OUlaZjE3RHQzanV4R0orUEN0OTJ3citvQQ # run only once per server: $(command -v python2 || command -v python2.7 || command -v python2.6) -c \\ "import BaseHTTPServer, SimpleHTTPServer; \\ SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map = {\'\': \'application/jose+json\'}; \\ s = BaseHTTPServer.HTTPServer((\'\', 4430), SimpleHTTPServer.SimpleHTTPRequestHandler); \\ s.serve_forever()" \n""") #self.assertTrue(validation in message) mock_verify.return_value = False self.assertEqual([None], self.auth.perform(self.achalls))