diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 59e99648d..66e98550e 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -249,8 +249,19 @@ def create_parser(plugins): help="Skip the end user license agreement screen.") add("-t", "--text", dest="text_mode", action="store_true", help="Use the text output instead of the curses UI.") - add("--test-mode", action="store_true", help=config_help("test_mode"), - default=flag_default("test_mode")) + + testing_group = parser.add_argument_group( + "testing", description="The following flags are meant for " + "testing purposes only! Do NOT change them, unless you " + "really know what you're doing!") + testing_group.add_argument( + "--no-verify-ssl", action="store_true", + help=config_help("no_verify_ssl"), + default=flag_default("no_verify_ssl")) + # TODO: apache and nginx plugins do NOT respect it + testing_group.add_argument( + "--dvsni-port", type=int, help=config_help("dvsni_port"), + default=flag_default("dvsni_port")) subparsers = parser.add_subparsers(metavar="SUBCOMMAND") def add_subparser(name, func): # pylint: disable=missing-docstring diff --git a/letsencrypt/client.py b/letsencrypt/client.py index 34347959d..c4b8ef40c 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -64,7 +64,7 @@ class Client(object): # TODO: Allow for other alg types besides RS256 self.network = network2.Network( config.server, jwk.JWKRSA.load(self.account.key.pem), - verify_ssl=(not config.test_mode)) + verify_ssl=config.no_verify_ssl) self.config = config diff --git a/letsencrypt/constants.py b/letsencrypt/constants.py index eb367e064..dacbe9040 100644 --- a/letsencrypt/constants.py +++ b/letsencrypt/constants.py @@ -22,7 +22,8 @@ CLI_DEFAULTS = dict( cert_path="/etc/letsencrypt/certs/cert-letsencrypt.pem", chain_path="/etc/letsencrypt/certs/chain-letsencrypt.pem", renewer_config_file="/etc/letsencrypt/renewer.conf", - test_mode=False, + no_verify_ssl=False, + dvsni_port=challenges.DVSNI.PORT, ) """Defaults for CLI flags and `.IConfig` attributes.""" @@ -84,7 +85,3 @@ IConfig.work_dir).""" NETSTAT = "/bin/netstat" """Location of netstat binary for checking whether a listener is already running on the specified port (Linux-specific).""" - -BOULDER_TEST_MODE_CHALLENGE_PORT = 5001 -"""Port that Boulder will connect on for validations in test mode.""" - diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 575332f3b..e47eea6cc 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -182,8 +182,11 @@ class IConfig(zope.interface.Interface): cert_path = zope.interface.Attribute("Let's Encrypt certificate file path.") chain_path = zope.interface.Attribute("Let's Encrypt chain file path.") - test_mode = zope.interface.Attribute( - "Test mode. Disables certificate verification.") + no_verify_ssl = zope.interface.Attribute( + "Disable SSL certificate verification.") + dvsni_port = zope.interface.Attribute( + "Port number to perform DVSNI challenge. " + "Boulder in testing mode defaults to 5001.") class IInstaller(IPlugin): diff --git a/letsencrypt/plugins/standalone/authenticator.py b/letsencrypt/plugins/standalone/authenticator.py index 3947c1e3e..599c34395 100644 --- a/letsencrypt/plugins/standalone/authenticator.py +++ b/letsencrypt/plugins/standalone/authenticator.py @@ -15,7 +15,6 @@ import zope.interface from acme import challenges from letsencrypt import achallenges -from letsencrypt import constants from letsencrypt import interfaces from letsencrypt.plugins import common @@ -379,10 +378,8 @@ class StandaloneAuthenticator(common.Plugin): results_if_failure.append(False) if not self.tasks: raise ValueError("nothing for .perform() to do") - port = challenges.DVSNI.PORT - if self.config and self.config.test_mode: - port = constants.BOULDER_TEST_MODE_CHALLENGE_PORT - if self.already_listening(port): + + if self.already_listening(self.config.port): # If we know a process is already listening on this port, # tell the user, and don't even attempt to bind it. (This # test is Linux-specific and won't indicate that the port @@ -390,7 +387,7 @@ class StandaloneAuthenticator(common.Plugin): return results_if_failure # Try to do the authentication; note that this creates # the listener subprocess via os.fork() - if self.start_listener(port, key): + if self.start_listener(self.config.port, key): return results_if_success else: # TODO: This should probably raise a DVAuthError exception @@ -427,8 +424,9 @@ class StandaloneAuthenticator(common.Plugin): def more_info(self): # pylint: disable=no-self-use """Human-readable string that describes the Authenticator.""" return ("The Standalone Authenticator uses PyOpenSSL to listen " - "on port 443 and perform DVSNI challenges. Once a certificate " + "on port {port} and perform DVSNI challenges. Once a certificate " "is attained, it will be saved in the " - "(TODO) current working directory.{0}{0}" - "TCP port 443 must be available in order to use the " - "Standalone Authenticator.".format(os.linesep)) + "(TODO) current working directory.{linesep}{linesep}" + "TCP port {port} must be available in order to use the " + "Standalone Authenticator.".format( + linesep=os.linesep, port=self.config.port)) diff --git a/letsencrypt/plugins/standalone/tests/authenticator_test.py b/letsencrypt/plugins/standalone/tests/authenticator_test.py index 802431536..005ba5682 100644 --- a/letsencrypt/plugins/standalone/tests/authenticator_test.py +++ b/letsencrypt/plugins/standalone/tests/authenticator_test.py @@ -22,6 +22,7 @@ KEY = le_util.Key("foo", pkg_resources.resource_string( "acme.jose", os.path.join("testdata", "rsa512_key.pem"))) PRIVATE_KEY = OpenSSL.crypto.load_privatekey( OpenSSL.crypto.FILETYPE_PEM, KEY.pem) +CONFIG = mock.Mock(port=5001) # Classes based on to allow interrupting infinite loop under test @@ -59,7 +60,7 @@ class ChallPrefTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) def test_chall_pref(self): self.assertEqual(self.authenticator.get_chall_pref("example.com"), @@ -71,7 +72,7 @@ class SNICallbackTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) self.cert = achallenges.DVSNI( challb=acme_util.DVSNI_P, domain="example.com", key=KEY).gen_cert_and_response()[0] @@ -109,7 +110,7 @@ class ClientSignalHandlerTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) self.authenticator.tasks = {"foononce.acme.invalid": "stuff"} self.authenticator.child_pid = 12345 @@ -138,7 +139,7 @@ class SubprocSignalHandlerTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) self.authenticator.tasks = {"foononce.acme.invalid": "stuff"} self.authenticator.child_pid = 12345 self.authenticator.parent_pid = 23456 @@ -190,7 +191,7 @@ class AlreadyListeningTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) @mock.patch("letsencrypt.plugins.standalone.authenticator.psutil." "net_connections") @@ -293,7 +294,7 @@ class PerformTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) self.achall1 = achallenges.DVSNI( challb=acme_util.chall_to_challb( @@ -327,7 +328,8 @@ class PerformTest(unittest.TestCase): self.assertTrue(isinstance(result[0], challenges.ChallengeResponse)) self.assertTrue(isinstance(result[1], challenges.ChallengeResponse)) self.assertFalse(result[2]) - self.authenticator.start_listener.assert_called_once_with(443, KEY) + self.authenticator.start_listener.assert_called_once_with( + CONFIG.port, KEY) def test_cannot_perform(self): """What happens if start_listener() returns False.""" @@ -342,7 +344,8 @@ class PerformTest(unittest.TestCase): self.assertTrue(isinstance(result, list)) self.assertEqual(len(result), 3) self.assertEqual(result, [None, None, False]) - self.authenticator.start_listener.assert_called_once_with(443, KEY) + self.authenticator.start_listener.assert_called_once_with( + CONFIG.port, KEY) def test_perform_with_pending_tasks(self): self.authenticator.tasks = {"foononce.acme.invalid": "cert_data"} @@ -367,7 +370,7 @@ class StartListenerTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) @mock.patch("letsencrypt.plugins.standalone.authenticator." "Crypto.Random.atfork") @@ -402,7 +405,7 @@ class DoParentProcessTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) @mock.patch("letsencrypt.plugins.standalone.authenticator." "zope.component.getUtility") @@ -444,7 +447,7 @@ class DoChildProcessTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) self.cert = achallenges.DVSNI( challb=acme_util.chall_to_challb( challenges.DVSNI(r=("x" * 32), nonce="abcdef"), "pending"), @@ -530,7 +533,7 @@ class CleanupTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import \ StandaloneAuthenticator - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) self.achall = achallenges.DVSNI( challb=acme_util.chall_to_challb( challenges.DVSNI(r="whee", nonce="foononce"), "pending"), @@ -562,7 +565,7 @@ class MoreInfoTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import ( StandaloneAuthenticator) - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) def test_more_info(self): """Make sure exceptions aren't raised.""" @@ -574,7 +577,7 @@ class InitTest(unittest.TestCase): def setUp(self): from letsencrypt.plugins.standalone.authenticator import ( StandaloneAuthenticator) - self.authenticator = StandaloneAuthenticator(config=None, name=None) + self.authenticator = StandaloneAuthenticator(config=CONFIG, name=None) def test_prepare(self): """Make sure exceptions aren't raised.