def setUp(self):
        super(DvsniPerformTest, self).setUp()

        config = util.get_nginx_configurator(self.config_path, self.config_dir,
                                             self.work_dir, self.ssl_options)

        rsa256_file = pkg_resources.resource_filename(
            "letsencrypt.client.tests", "testdata/rsa256_key.pem")
        rsa256_pem = pkg_resources.resource_string("letsencrypt.client.tests",
                                                   "testdata/rsa256_key.pem")

        auth_key = le_util.Key(rsa256_file, rsa256_pem)

        from letsencrypt.client.plugins.nginx import dvsni
        self.sni = dvsni.NginxDvsni(config)

        self.achalls = [
            achallenges.DVSNI(chall=challenges.DVSNI(
                r="foo",
                nonce="bar",
            ),
                              domain="www.example.com",
                              key=auth_key),
            achallenges.DVSNI(chall=challenges.DVSNI(
                r="\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y\x80"
                "\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945",
                nonce="Y\xed\x01L\xac\x95\xf7pW\xb1\xd7"
                "\xa1\xb2\xc5\x96\xba",
            ),
                              domain="blah",
                              key=auth_key),
        ]
    def setUp(self):
        super(DvsniPerformTest, self).setUp()

        with mock.patch("letsencrypt.client.plugins.apache.configurator."
                        "mod_loaded") as mock_load:
            mock_load.return_value = True
            config = util.get_apache_configurator(
                self.config_path, self.config_dir, self.work_dir,
                self.ssl_options)

        from letsencrypt.client.plugins.apache import dvsni
        self.sni = dvsni.ApacheDvsni(config)

        rsa256_file = pkg_resources.resource_filename(
            "letsencrypt.client.tests", "testdata/rsa256_key.pem")
        rsa256_pem = pkg_resources.resource_string(
            "letsencrypt.client.tests", "testdata/rsa256_key.pem")

        auth_key = le_util.Key(rsa256_file, rsa256_pem)
        self.achalls = [
            achallenges.DVSNI(
                chall=challenges.DVSNI(
                    r="\x8c\x8a\xbf_-f\\cw\xee\xd6\xf8/\xa5\xe3\xfd\xeb9\xf1"
                      "\xf5\xb9\xefVM\xc9w\xa4u\x9c\xe1\x87\xb4",
                    nonce="7\xbc^\xb7]>\x00\xa1\x9bOcU\x84^Z\x18",
                ), domain="encryption-example.demo", key=auth_key),
            achallenges.DVSNI(
                chall=challenges.DVSNI(
                    r="\xba\xa9\xda?<m\xaewmx\xea\xad\xadv\xf4\x02\xc9y\x80"
                      "\xe2_X\t\xe7\xc7\xa4\t\xca\xf7&\x945",
                    nonce="Y\xed\x01L\xac\x95\xf7pW\xb1\xd7"
                          "\xa1\xb2\xc5\x96\xba",
                ), domain="letsencrypt.demo", key=auth_key),
        ]
示例#3
0
    def test_perform(self, mock_restart, mock_dvsni_perform):
        # Only tests functionality specific to configurator.perform
        # Note: As more challenges are offered this will have to be expanded
        auth_key = le_util.Key(self.rsa256_file, self.rsa256_pem)
        chall1 = challenge_util.DvsniChall(
            "encryption-example.demo",
            "jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q",
            "37bc5eb75d3e00a19b4f6355845e5a18", auth_key)
        chall2 = challenge_util.DvsniChall(
            "letsencrypt.demo", "uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU",
            "59ed014cac95f77057b1d7a1b2c596ba", auth_key)

        dvsni_ret_val = [{
            "type": "dvsni",
            "s": "randomS1"
        }, {
            "type": "dvsni",
            "s": "randomS2"
        }]

        mock_dvsni_perform.return_value = dvsni_ret_val
        responses = self.config.perform([chall1, chall2])

        self.assertEqual(mock_dvsni_perform.call_count, 1)
        self.assertEqual(responses, dvsni_ret_val)

        self.assertEqual(mock_restart.call_count, 1)
    def test_perform(self, mock_restart, mock_dvsni_perform):
        # Only tests functionality specific to configurator.perform
        # Note: As more challenges are offered this will have to be expanded
        auth_key = le_util.Key(self.rsa256_file, self.rsa256_pem)
        achall1 = achallenges.DVSNI(chall=challenges.DVSNI(
            r="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q",
            nonce="37bc5eb75d3e00a19b4f6355845e5a18"),
                                    domain="encryption-example.demo",
                                    key=auth_key)
        achall2 = achallenges.DVSNI(chall=challenges.DVSNI(
            r="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU",
            nonce="59ed014cac95f77057b1d7a1b2c596ba"),
                                    domain="letsencrypt.demo",
                                    key=auth_key)

        dvsni_ret_val = [
            challenges.DVSNIResponse(s="randomS1"),
            challenges.DVSNIResponse(s="randomS2"),
        ]

        mock_dvsni_perform.return_value = dvsni_ret_val
        responses = self.config.perform([achall1, achall2])

        self.assertEqual(mock_dvsni_perform.call_count, 1)
        self.assertEqual(responses, dvsni_ret_val)

        self.assertEqual(mock_restart.call_count, 1)
示例#5
0
    def setUp(self):
        super(DvsniPerformTest, self).setUp()

        with mock.patch("letsencrypt.client.apache.configurator."
                        "mod_loaded") as mock_load:
            mock_load.return_value = True
            config = util.get_apache_configurator(
                self.config_path, self.config_dir, self.work_dir,
                self.ssl_options)

        from letsencrypt.client.apache import dvsni
        self.sni = dvsni.ApacheDvsni(config)

        rsa256_file = pkg_resources.resource_filename(
            "letsencrypt.client.tests", 'testdata/rsa256_key.pem')
        rsa256_pem = pkg_resources.resource_string(
            "letsencrypt.client.tests", 'testdata/rsa256_key.pem')

        auth_key = le_util.Key(rsa256_file, rsa256_pem)
        self.challs = []
        self.challs.append(challenge_util.DvsniChall(
            "encryption-example.demo",
            "jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q",
            "37bc5eb75d3e00a19b4f6355845e5a18",
            auth_key))
        self.challs.append(challenge_util.DvsniChall(
            "letsencrypt.demo",
            "uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU",
            "59ed014cac95f77057b1d7a1b2c596ba",
            auth_key))
示例#6
0
def init_key(key_size, key_dir):
    """Initializes privkey.

    Inits key and CSR using provided files or generating new files
    if necessary. Both will be saved in PEM format on the
    filesystem. The CSR is placed into DER format to allow
    the namedtuple to easily work with the protocol.

    :param str key_dir: Key save directory.

    """
    try:
        key_pem = crypto_util.make_key(key_size)
    except ValueError as err:
        logging.fatal(str(err))
        sys.exit(1)

    # Save file
    le_util.make_or_verify_dir(key_dir, 0o700)
    key_f, key_filename = le_util.unique_file(
        os.path.join(key_dir, "key-letsencrypt.pem"), 0o600)
    key_f.write(key_pem)
    key_f.close()

    logging.info("Generating key (%d bits): %s", key_size, key_filename)

    return le_util.Key(key_filename, key_pem)
 def test_can_perform(self):
     """What happens if start_listener() returns True."""
     test_key = pkg_resources.resource_string(__name__,
                                              "testdata/rsa256_key.pem")
     key = le_util.Key("something", test_key)
     chall1 = challenge_util.DvsniChall("foo.example.com", "whee",
                                        "foononce", key)
     chall2 = challenge_util.DvsniChall("bar.example.com", "whee",
                                        "barnonce", key)
     bad_chall = ("This", "Represents", "A Non-DVSNI", "Challenge")
     self.authenticator.start_listener = mock.Mock()
     self.authenticator.start_listener.return_value = True
     result = self.authenticator.perform([chall1, chall2, bad_chall])
     self.assertEqual(len(self.authenticator.tasks), 2)
     self.assertTrue(
         self.authenticator.tasks.has_key("foononce.acme.invalid"))
     self.assertTrue(
         self.authenticator.tasks.has_key("barnonce.acme.invalid"))
     self.assertTrue(isinstance(result, list))
     self.assertEqual(len(result), 3)
     self.assertTrue(isinstance(result[0], dict))
     self.assertTrue(isinstance(result[1], dict))
     self.assertFalse(result[2])
     self.assertTrue(result[0].has_key("s"))
     self.assertTrue(result[1].has_key("s"))
     self.authenticator.start_listener.assert_called_once_with(443, key)
示例#8
0
    def test_perform(self, mock_restart, mock_dvsni_perform):
        # Only tests functionality specific to configurator.perform
        # Note: As more challenges are offered this will have to be expanded
        auth_key = le_util.Key(self.rsa256_file, self.rsa256_pem)
        achall1 = achallenges.DVSNI(
            chall=challenges.DVSNI(
                r="foo",
                nonce="bar"),
            domain="localhost", key=auth_key)
        achall2 = achallenges.DVSNI(
            chall=challenges.DVSNI(
                r="abc",
                nonce="def"),
            domain="example.com", key=auth_key)

        dvsni_ret_val = [
            challenges.DVSNIResponse(s="irrelevant"),
            challenges.DVSNIResponse(s="arbitrary"),
        ]

        mock_dvsni_perform.return_value = dvsni_ret_val
        responses = self.config.perform([achall1, achall2])

        self.assertEqual(mock_dvsni_perform.call_count, 1)
        self.assertEqual(responses, dvsni_ret_val)
        self.assertEqual(mock_restart.call_count, 1)
    def _from_config_fp(cls, config, config_fp):
        try:
            acc_config = configobj.ConfigObj(infile=config_fp,
                                             file_error=True,
                                             create_empty=False)
        except IOError:
            raise errors.LetsEncryptClientError(
                "Account for %s does not exist" % os.path.basename(config_fp))

        if os.path.basename(config_fp) != "default":
            email = os.path.basename(config_fp)
        else:
            email = None
        phone = acc_config["phone"] if acc_config["phone"] != "None" else None

        with open(acc_config["key"]) as key_file:
            key = le_util.Key(acc_config["key"], key_file.read())

        if "RegistrationResource" in acc_config:
            acc_config_rr = acc_config["RegistrationResource"]
            regr = messages2.RegistrationResource(
                uri=acc_config_rr["uri"],
                new_authzr_uri=acc_config_rr["new_authzr_uri"],
                terms_of_service=acc_config_rr["terms_of_service"],
                body=messages2.Registration.from_json(acc_config_rr["body"]))
        else:
            regr = None

        return cls(config, key, email, phone, regr)
示例#10
0
    def setUp(self):
        self.chall = challenges.DVSNI(r="r_value", nonce="12345ABCDE")
        self.response = challenges.DVSNIResponse()
        key = le_util.Key(
            "path",
            pkg_resources.resource_string(
                __name__, os.path.join("testdata", "rsa256_key.pem")))

        from letsencrypt.client.achallenges import DVSNI
        self.achall = DVSNI(chall=self.chall, domain="example.com", key=key)
 def test_perform_when_already_listening(self):
     test_key = pkg_resources.resource_string(__name__,
                                              "testdata/rsa256_key.pem")
     key = le_util.Key("something", test_key)
     chall1 = challenge_util.DvsniChall("foo.example.com", "whee",
                                        "foononce", key)
     self.authenticator.already_listening = mock.Mock()
     self.authenticator.already_listening.return_value = True
     result = self.authenticator.perform([chall1])
     self.assertEqual(result, [None])
    def setUp(self):
        from letsencrypt.client.revoker import Revoker
        super(RevokerTest, self).setUp()

        with open(self.key_path) as key_file:
            self.key = le_util.Key(self.key_path, key_file.read())

        self._store_certs()

        self.revoker = Revoker(
            mock.MagicMock(spec=configurator.ApacheConfigurator),
            self.mock_config)
    def test_revoke_by_wrong_key(self, mock_display, mock_net):
        mock_display().confirm_revocation.return_value = True

        key_path = pkg_resources.resource_filename(
            "letsencrypt.client.tests",
            os.path.join("testdata", "rsa256_key.pem"))

        wrong_key = le_util.Key(key_path, open(key_path).read())
        self.revoker.revoke_from_key(wrong_key)

        # Nothing was removed
        self.assertEqual(len(self._get_rows()), 2)
        # No revocation went through
        self.assertEqual(mock_net.call_count, 0)
示例#14
0
    def test_standard(self):
        """Basic test for straightline code."""
        domain = "example.com"
        dvsni_r = "r_value"
        r_b64 = jose.b64encode(dvsni_r)
        pem = pkg_resources.resource_string(
            __name__, os.path.join("testdata", "rsa256_key.pem"))
        key = le_util.Key("path", pem)
        nonce = "12345ABCDE"
        cert_pem, s_b64 = self._call(domain, r_b64, nonce, key)

        # pylint: disable=protected-access
        ext = challenge_util._dvsni_gen_ext(dvsni_r, jose.b64decode(s_b64))
        self._standard_check_cert(cert_pem, domain, nonce, ext)
 def setUp(self):
     from letsencrypt.client.standalone_authenticator import \
         StandaloneAuthenticator
     self.authenticator = StandaloneAuthenticator()
     name, r_b64 = "example.com", jose.b64encode("x" * 32)
     test_key = pkg_resources.resource_string(__name__,
                                              "testdata/rsa256_key.pem")
     nonce, key = "abcdef", le_util.Key("foo", test_key)
     self.cert = challenge_util.dvsni_gen_cert(name, r_b64, nonce, key)[0]
     private_key = OpenSSL.crypto.load_privatekey(
         OpenSSL.crypto.FILETYPE_PEM, key.pem)
     self.authenticator.private_key = private_key
     self.authenticator.tasks = {"abcdef.acme.invalid": self.cert}
     self.authenticator.child_pid = 12345
示例#16
0
 def setUp(self):
     from letsencrypt.client.standalone_authenticator import \
         StandaloneAuthenticator
     self.authenticator = StandaloneAuthenticator()
     test_key = pkg_resources.resource_string(
         __name__, "testdata/rsa256_key.pem")
     key = le_util.Key("foo", test_key)
     self.cert = achallenges.DVSNI(
         chall=challenges.DVSNI(r="x"*32, nonce="abcdef"),
         domain="example.com", key=key).gen_cert_and_response()[0]
     private_key = OpenSSL.crypto.load_privatekey(
         OpenSSL.crypto.FILETYPE_PEM, key.pem)
     self.authenticator.private_key = private_key
     self.authenticator.tasks = {"abcdef.acme.invalid": self.cert}
     self.authenticator.child_pid = 12345
示例#17
0
    def setUp(self):
        from letsencrypt.client.standalone_authenticator import \
            StandaloneAuthenticator
        self.authenticator = StandaloneAuthenticator()

        test_key = pkg_resources.resource_string(
            __name__, "testdata/rsa256_key.pem")
        self.key = le_util.Key("something", test_key)

        self.achall1 = achallenges.DVSNI(
            chall=challenges.DVSNI(r="whee", nonce="foo"),
            domain="foo.example.com", key=self.key)
        self.achall2 = achallenges.DVSNI(
            chall=challenges.DVSNI(r="whee", nonce="bar"),
            domain="bar.example.com", key=self.key)
        bad_achall = ("This", "Represents", "A Non-DVSNI", "Challenge")
        self.achalls = [self.achall1, self.achall2, bad_achall]
示例#18
0
    def setUp(self):
        zope.component.provideUtility(display_util.FileDisplay(sys.stdout))

        self.accounts_dir = tempfile.mkdtemp("accounts")
        self.account_keys_dir = os.path.join(self.accounts_dir, "keys")
        os.makedirs(self.account_keys_dir, 0o700)

        self.config = mock.MagicMock(accounts_dir=self.accounts_dir,
                                     account_keys_dir=self.account_keys_dir,
                                     server="letsencrypt-demo.org")
        self.key = le_util.Key("keypath", "pem")

        self.acc1 = account.Account(self.config, self.key, "*****@*****.**")
        self.acc2 = account.Account(self.config, self.key, "*****@*****.**",
                                    "phone")
        self.acc1.save()
        self.acc2.save()
示例#19
0
def revoke(config, no_confirm, cert, authkey):
    """Revoke certificates.

    :param config: Configuration.
    :type config: :class:`letsencrypt.client.interfaces.IConfig`

    """
    # Misconfigurations don't really matter. Determine installer better choose
    # correctly though.
    # This will need some better prepared or properly configured parameter...
    # I will figure it out later...
    installer = determine_installer(config)

    revoc = revoker.Revoker(installer, config, no_confirm)
    # Cert is most selective, so it is chosen first.
    if cert is not None:
        revoc.revoke_from_cert(cert[0])
    elif authkey is not None:
        revoc.revoke_from_key(le_util.Key(authkey[0], authkey[1]))
    else:
        revoc.revoke_from_menu()
    def setUp(self):
        from letsencrypt.client.auth_handler import AuthHandler

        self.mock_dv_auth = mock.MagicMock(name="ApacheConfigurator")
        self.mock_cont_auth = mock.MagicMock(name="ContinuityAuthenticator")

        self.mock_dv_auth.get_chall_pref.return_value = [challenges.DVSNI]
        self.mock_cont_auth.get_chall_pref.return_value = [
            challenges.RecoveryToken
        ]

        self.mock_cont_auth.perform.side_effect = gen_auth_resp
        self.mock_dv_auth.perform.side_effect = gen_auth_resp

        self.mock_account = mock.Mock(key=le_util.Key("file_path", "PEM"))
        self.mock_net = mock.MagicMock(spec=network2.Network)

        self.handler = AuthHandler(self.mock_dv_auth, self.mock_cont_auth,
                                   self.mock_net, self.mock_account)

        logging.disable(logging.CRITICAL)
    def determine_account(self, mock_op, mock_prompt):
        """Test determine account"""
        from letsencrypt.client import client

        key = le_util.Key("file", "pem")
        test_acc = account.Account(self.config, key, "*****@*****.**")
        mock_op.return_value = test_acc

        # Test 0
        mock_prompt.return_value = None
        self.assertTrue(client.determine_account(self.config) is None)

        # Test 1
        test_acc.save()
        acc = client.determine_account(self.config)
        self.assertEqual(acc.email, test_acc.email)

        # Test multiple
        self.assertFalse(mock_op.called)
        acc2 = account.Account(self.config, key)
        acc2.save()
        chosen_acc = client.determine_account(self.config)
        self.assertTrue(mock_op.called)
        self.assertTrue(chosen_acc.email, test_acc.email)
import socket
import unittest

import mock
import OpenSSL.crypto
import OpenSSL.SSL

from letsencrypt.acme import challenges

from letsencrypt.client import achallenges
from letsencrypt.client import le_util

from letsencrypt.client.tests import acme_util


KEY = le_util.Key("foo", pkg_resources.resource_string(
    "letsencrypt.acme.jose", os.path.join("testdata", "rsa512_key.pem")))
PRIVATE_KEY = OpenSSL.crypto.load_privatekey(
    OpenSSL.crypto.FILETYPE_PEM, KEY.pem)


# Classes based on to allow interrupting infinite loop under test
# after one iteration, based on.
# http://igorsobreira.com/2013/03/17/testing-infinite-loops.html

class _SocketAcceptOnlyNTimes(object):
    # pylint: disable=too-few-public-methods
    """
    Callable that will raise `CallableExhausted`
    exception after `limit` calls, modified to also return
    a tuple simulating the return values of a socket.accept()
    call
示例#23
0
def main():  # pylint: disable=too-many-branches, too-many-statements
    """Command line argument parsing and main script execution."""
    # note: arg parser internally handles --help (and exits afterwards)
    args = create_parser().parse_args()
    config = configuration.NamespaceConfig(args)

    # note: check is done after arg parsing as --help should work w/o root also.
    if not os.geteuid() == 0:
        sys.exit(
            "{0}Root is required to run letsencrypt.  Please use sudo.{0}".
            format(os.linesep))

    # Set up logging
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    if args.use_curses:
        logger.addHandler(log.DialogHandler())
        displayer = display_util.NcursesDisplay()
    else:
        displayer = display_util.FileDisplay(sys.stdout)

    zope.component.provideUtility(displayer)

    if args.view_config_changes:
        client.view_config_changes(config)
        sys.exit()

    if args.revoke or args.rev_cert is not None or args.rev_key is not None:
        client.revoke(config, args.no_confirm, args.rev_cert, args.rev_key)
        sys.exit()

    if args.rollback > 0:
        client.rollback(args.rollback, config)
        sys.exit()

    if not args.eula:
        display_eula()

    all_auths = init_auths(config)
    logging.debug('Initialized authenticators: %s', all_auths.keys())
    try:
        auth = client.determine_authenticator(all_auths, config)
        logging.debug("Selected authenticator: %s", auth)
    except errors.LetsEncryptClientError as err:
        logging.critical(str(err))
        sys.exit(1)

    if auth is None:
        sys.exit(0)

    # Use the same object if possible
    if interfaces.IInstaller.providedBy(auth):  # pylint: disable=no-member
        installer = auth
    else:
        # This is simple and avoids confusion right now.
        installer = None

    if args.domains is None:
        doms = display_ops.choose_names(installer)
    else:
        doms = args.domains

    if not doms:
        sys.exit(0)

    # Prepare for init of Client
    if args.authkey is None:
        authkey = client.init_key(args.rsa_key_size, config.key_dir)
    else:
        authkey = le_util.Key(args.authkey[0], args.authkey[1])

    acme = client.Client(config, authkey, auth, installer)

    # Validate the key and csr
    client.validate_key_csr(authkey)

    # This more closely mimics the capabilities of the CLI
    # It should be possible for reconfig only, install-only, no-install
    # I am not sure the best way to handle all of the unimplemented abilities,
    # but this code should be safe on all environments.
    cert_file = None
    if auth is not None:
        cert_file, chain_file = acme.obtain_certificate(doms)
    if installer is not None and cert_file is not None:
        acme.deploy_certificate(doms, authkey, cert_file, chain_file)
    if installer is not None:
        acme.enhance_config(doms, args.redirect)
示例#24
0
def main():  # pylint: disable=too-many-branches
    """Command line argument parsing and main script execution."""
    # note: arg parser internally handles --help (and exits afterwards)
    args = create_parser().parse_args()
    config = configuration.NamespaceConfig(args)

    # note: check is done after arg parsing as --help should work w/o root also.
    if not os.geteuid() == 0:
        sys.exit(
            "{0}Root is required to run letsencrypt.  Please use sudo.{0}".
            format(os.linesep))

    # Set up logging
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    if args.use_curses:
        logger.addHandler(log.DialogHandler())
        displayer = display.NcursesDisplay()
    else:
        displayer = display.FileDisplay(sys.stdout)
    zope.component.provideUtility(displayer)

    if args.view_config_changes:
        client.view_config_changes(config)
        sys.exit()

    if args.revoke:
        client.revoke(config)
        sys.exit()

    if args.rollback > 0:
        client.rollback(args.rollback, config)
        sys.exit()

    if not args.eula:
        display_eula()

    # Make sure we actually get an installer that is functioning properly
    # before we begin to try to use it.
    try:
        installer = client.determine_installer(config)
    except errors.LetsEncryptMisconfigurationError as err:
        logging.fatal(
            "Please fix your configuration before proceeding.  "
            "The Installer exited with the following message: "
            "%s", err)
        sys.exit(1)

    # Use the same object if possible
    if interfaces.IAuthenticator.providedBy(installer):  # pylint: disable=no-member
        auth = installer
    else:
        auth = client.determine_authenticator(config)

    doms = choose_names(installer) if args.domains is None else args.domains

    # Prepare for init of Client
    if args.privkey is None:
        privkey = client.init_key(args.rsa_key_size, config.key_dir)
    else:
        privkey = le_util.Key(args.privkey[0], args.privkey[1])

    acme = client.Client(config, privkey, auth, installer)

    # Validate the key and csr
    client.validate_key_csr(privkey)

    # This more closely mimics the capabilities of the CLI
    # It should be possible for reconfig only, install-only, no-install
    # I am not sure the best way to handle all of the unimplemented abilities,
    # but this code should be safe on all environments.
    if auth is not None:
        cert_file, chain_file = acme.obtain_certificate(doms)
    if installer is not None and cert_file is not None:
        acme.deploy_certificate(doms, privkey, cert_file, chain_file)
    if installer is not None:
        acme.enhance_config(doms, args.redirect)