Example #1
0
def get_nginx_configurator(config_path,
                           config_dir,
                           work_dir,
                           version=(1, 6, 2)):
    """Create an Nginx Configurator with the specified options."""

    backups = os.path.join(work_dir, "backups")

    with mock.patch("letsencrypt_nginx.configurator.le_util."
                    "exe_exists") as mock_exe_exists:
        mock_exe_exists.return_value = True

        config = configurator.NginxConfigurator(config=mock.MagicMock(
            nginx_server_root=config_path,
            le_vhost_ext="-le-ssl.conf",
            config_dir=config_dir,
            work_dir=work_dir,
            backup_dir=backups,
            temp_checkpoint_dir=os.path.join(work_dir, "temp_checkpoints"),
            in_progress_dir=os.path.join(backups, "IN_PROGRESS"),
            server="https://acme-server.org:443/new",
            tls_sni_01_port=5001,
        ),
                                                name="nginx",
                                                version=version)
        config.prepare()

    # Provide general config utility.
    nsconfig = configuration.NamespaceConfig(config.config)
    zope.component.provideUtility(nsconfig)

    return config
Example #2
0
 def setUp(self):
     self.args = mock.MagicMock(account=None,
                                email=None,
                                register_unsafely_without_email=False)
     self.config = configuration.NamespaceConfig(self.args)
     self.accs = [mock.MagicMock(id='x'), mock.MagicMock(id='y')]
     self.account_storage = account.AccountMemoryStorage()
Example #3
0
def main(cli_args=sys.argv[1:]):
    """Command line argument parsing and main script execution."""
    sys.excepthook = functools.partial(_handle_exception, args=None)

    # note: arg parser internally handles --help (and exits afterwards)
    plugins = plugins_disco.PluginsRegistry.find_all()
    parser, tweaked_cli_args = create_parser(plugins, cli_args)
    args = parser.parse_args(tweaked_cli_args)
    config = configuration.NamespaceConfig(args)
    zope.component.provideUtility(config)

    # Setup logging ASAP, otherwise "No handlers could be found for
    # logger ..." TODO: this should be done before plugins discovery
    for directory in config.config_dir, config.work_dir:
        le_util.make_or_verify_dir(
            directory, constants.CONFIG_DIRS_MODE, os.geteuid(),
            "--strict-permissions" in cli_args)
    # TODO: logs might contain sensitive data such as contents of the
    # private key! #525
    le_util.make_or_verify_dir(
        args.logs_dir, 0o700, os.geteuid(), "--strict-permissions" in cli_args)
    setup_logging(args, _cli_log_handler, logfile='letsencrypt.log')

    # do not log `args`, as it contains sensitive data (e.g. revoke --key)!
    logger.debug("Arguments: %r", cli_args)
    logger.debug("Discovered plugins: %r", plugins)

    sys.excepthook = functools.partial(_handle_exception, args=args)

    # Displayer
    if args.text_mode:
        displayer = display_util.FileDisplay(sys.stdout)
    else:
        displayer = display_util.NcursesDisplay()
    zope.component.provideUtility(displayer)

    # Reporter
    report = reporter.Reporter()
    zope.component.provideUtility(report)
    atexit.register(report.atexit_print_messages)

    # TODO: remove developer EULA prompt for the launch
    if not config.eula:
        eula = pkg_resources.resource_string("letsencrypt", "EULA")
        if not zope.component.getUtility(interfaces.IDisplay).yesno(
                eula, "Agree", "Cancel"):
            raise Error("Must agree to TOS")

    if not os.geteuid() == 0:
        logger.warning(
            "Root (sudo) is required to run most of letsencrypt functionality.")
        # check must be done after arg parsing as --help should work
        # w/o root; on the other hand, e.g. "letsencrypt run
        # --authenticator dns" or "letsencrypt plugins" does not
        # require root as well
        #return (
        #    "{0}Root is required to run letsencrypt.  Please use sudo.{0}"
        #    .format(os.linesep))

    return args.func(args, config, plugins)
Example #4
0
def renew(cert, old_version):
    """Perform automated renewal of the referenced cert, if possible.

    :param letsencrypt.storage.RenewableCert cert: The certificate
        lineage to attempt to renew.
    :param int old_version: The version of the certificate lineage
        relative to which the renewal should be attempted.

    :returns: A number referring to newly created version of this cert
        lineage, or ``False`` if renewal was not successful.
    :rtype: `int` or `bool`

    """
    # TODO: handle partial success (some names can be renewed but not
    #       others)
    # TODO: handle obligatory key rotation vs. optional key rotation vs.
    #       requested key rotation
    if "renewalparams" not in cert.configfile:
        # TODO: notify user?
        return False
    renewalparams = cert.configfile["renewalparams"]
    if "authenticator" not in renewalparams:
        # TODO: notify user?
        return False
    # Instantiate the appropriate authenticator
    plugins = plugins_disco.PluginsRegistry.find_all()
    config = configuration.NamespaceConfig(_AttrDict(renewalparams))
    # XXX: this loses type data (for example, the fact that key_size
    #      was an int, not a str)
    config.rsa_key_size = int(config.rsa_key_size)
    config.dvsni_port = int(config.dvsni_port)
    try:
        authenticator = plugins[renewalparams["authenticator"]]
    except KeyError:
        # TODO: Notify user? (authenticator could not be found)
        return False
    authenticator = authenticator.init(config)

    authenticator.prepare()
    account = client.determine_account(config)
    # TODO: are there other ways to get the right account object, e.g.
    #       based on the email parameter that might be present in
    #       renewalparams?

    our_client = client.Client(config, account, authenticator, None)
    with open(cert.version("cert", old_version)) as f:
        sans = crypto_util.get_sans_from_cert(f.read())
    new_certr, new_chain, new_key, _ = our_client.obtain_certificate(sans)
    if new_chain is not None:
        # XXX: Assumes that there was no key change.  We need logic
        #      for figuring out whether there was or not.  Probably
        #      best is to have obtain_certificate return None for
        #      new_key if the old key is to be used (since save_successor
        #      already understands this distinction!)
        return cert.save_successor(old_version, new_certr.body.as_pem(),
                                   new_key.pem, new_chain.as_pem())
        # TODO: Notify results
    else:
        # TODO: Notify negative results
        return False
Example #5
0
 def test_ancient_webroot_renewal_conf(self, mock_set_by_cli):
     mock_set_by_cli.return_value = False
     rc_path = self._make_test_renewal_conf('sample-renewal-ancient.conf')
     args = mock.MagicMock(account=None, email=None, webroot_path=None)
     config = configuration.NamespaceConfig(args)
     lineage = storage.RenewableCert(
         rc_path, configuration.RenewerConfiguration(config))
     renewalparams = lineage.configuration["renewalparams"]
     # pylint: disable=protected-access
     renewal._restore_webroot_config(config, renewalparams)
     self.assertEqual(config.webroot_path, ["/var/www/"])
Example #6
0
    def _prepare_configurator(self, server_root, config_file):
        """Prepares the Apache plugin for testing"""
        self.le_config.apache_server_root = server_root
        self.le_config.apache_ctl = "apachectl -d {0} -f {1}".format(
            server_root, config_file)
        self.le_config.apache_enmod = "a2enmod.sh {0}".format(server_root)
        self.le_config.apache_init_script = self.le_config.apache_ctl + " -k"

        self._apache_configurator = configurator.ApacheConfigurator(
            config=configuration.NamespaceConfig(self.le_config),
            name="apache")
        self._apache_configurator.prepare()
Example #7
0
def main(cli_args=sys.argv[1:]):
    """Command line argument parsing and main script execution."""
    sys.excepthook = functools.partial(_handle_exception, config=None)
    plugins = plugins_disco.PluginsRegistry.find_all()

    # note: arg parser internally handles --help (and exits afterwards)
    args = cli.prepare_and_parse_args(plugins, cli_args)
    config = configuration.NamespaceConfig(args)
    zope.component.provideUtility(config)

    # Setup logging ASAP, otherwise "No handlers could be found for
    # logger ..." TODO: this should be done before plugins discovery
    for directory in config.config_dir, config.work_dir:
        le_util.make_or_verify_dir(directory, constants.CONFIG_DIRS_MODE,
                                   os.geteuid(), "--strict-permissions"
                                   in cli_args)
    # TODO: logs might contain sensitive data such as contents of the
    # private key! #525
    le_util.make_or_verify_dir(config.logs_dir, 0o700, os.geteuid(),
                               "--strict-permissions" in cli_args)
    setup_logging(config, _cli_log_handler, logfile='letsencrypt.log')

    logger.debug("letsencrypt version: %s", letsencrypt.__version__)
    # do not log `config`, as it contains sensitive data (e.g. revoke --key)!
    logger.debug("Arguments: %r", cli_args)
    logger.debug("Discovered plugins: %r", plugins)

    sys.excepthook = functools.partial(_handle_exception, config=config)

    # Displayer
    if config.quiet:
        config.noninteractive_mode = True
        displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w"))
    elif config.noninteractive_mode:
        displayer = display_util.NoninteractiveDisplay(sys.stdout)
    elif config.text_mode:
        displayer = display_util.FileDisplay(sys.stdout)
    elif config.verb == "renew":
        config.noninteractive_mode = True
        displayer = display_util.NoninteractiveDisplay(sys.stdout)
    else:
        displayer = display_util.NcursesDisplay()
    zope.component.provideUtility(displayer)

    # Reporter
    report = reporter.Reporter(config)
    zope.component.provideUtility(report)
    atexit.register(report.atexit_print_messages)

    return config.func(config, plugins)
Example #8
0
def main(args=sys.argv[1:]):
    """Command line argument parsing and main script execution."""
    # note: arg parser internally handles --help (and exits afterwards)
    plugins = plugins_disco.PluginsRegistry.find_all()
    args = create_parser(plugins).parse_args(args)
    config = configuration.NamespaceConfig(args)

    # Displayer
    if args.text_mode:
        displayer = display_util.FileDisplay(sys.stdout)
    else:
        displayer = display_util.NcursesDisplay()
    zope.component.provideUtility(displayer)

    # Reporter
    report = reporter.Reporter()
    zope.component.provideUtility(report)
    atexit.register(report.atexit_print_messages)

    # Logging
    level = -args.verbose_count * 10
    logger = logging.getLogger()
    logger.setLevel(level)
    logging.debug("Logging level set at %d", level)
    if not args.text_mode:
        logger.addHandler(log.DialogHandler())

    logging.debug("Discovered plugins: %r", plugins)

    if not os.geteuid() == 0:
        logging.warning(
            "Root (sudo) is required to run most of letsencrypt functionality.")
        # check must be done after arg parsing as --help should work
        # w/o root; on the other hand, e.g. "letsencrypt run
        # --authenticator dns" or "letsencrypt plugins" does not
        # require root as well
        #return (
        #    "{0}Root is required to run letsencrypt.  Please use sudo.{0}"
        #    .format(os.linesep))

    return args.func(args, config, plugins)
Example #9
0
def main(cli_args=sys.argv[1:]):
    """Command line argument parsing and main script execution."""
    # note: arg parser internally handles --help (and exits afterwards)
    plugins = plugins_disco.PluginsRegistry.find_all()
    args = create_parser(plugins, cli_args).parse_args(cli_args)
    config = configuration.NamespaceConfig(args)

    # Setup logging ASAP, otherwise "No handlers could be found for
    # logger ..." TODO: this should be done before plugins discovery
    for directory in config.config_dir, config.work_dir:
        le_util.make_or_verify_dir(
            directory, constants.CONFIG_DIRS_MODE, os.geteuid())
    # TODO: logs might contain sensitive data such as contents of the
    # private key! #525
    le_util.make_or_verify_dir(args.logs_dir, 0o700, os.geteuid())
    _setup_logging(args)

    def handle_exception_common():
        """Logs the exception and reraises it if in debug mode."""
        logger.debug("Exiting abnormally", exc_info=True)
        if args.debug:
            raise

    try:
        return main2(cli_args, args, config, plugins)
    except errors.Error as error:
        handle_exception_common()
        return error
    except KeyboardInterrupt:
        handle_exception_common()
        # Ensures a new line is printed
        return ""
    except: # pylint: disable=bare-except
        handle_exception_common()
        return ("An unexpected error occured. Please see the logfiles in {0} "
                "for more details.".format(args.logs_dir))
Example #10
0
 def setUp(self):
     self.args = mock.MagicMock(account=None, email=None)
     self.config = configuration.NamespaceConfig(self.args)
     self.accs = [mock.MagicMock(id="x"), mock.MagicMock(id="y")]
     self.account_storage = account.AccountMemoryStorage()
Example #11
0
def renew(cert, old_version):
    """Perform automated renewal of the referenced cert, if possible.

    :param letsencrypt.storage.RenewableCert cert: The certificate
        lineage to attempt to renew.
    :param int old_version: The version of the certificate lineage
        relative to which the renewal should be attempted.

    :returns: A number referring to newly created version of this cert
        lineage, or ``False`` if renewal was not successful.
    :rtype: `int` or `bool`

    """
    # TODO: handle partial success (some names can be renewed but not
    #       others)
    # TODO: handle obligatory key rotation vs. optional key rotation vs.
    #       requested key rotation
    if "renewalparams" not in cert.configfile:
        # TODO: notify user?
        return False
    renewalparams = cert.configfile["renewalparams"]
    if "authenticator" not in renewalparams:
        # TODO: notify user?
        return False
    # Instantiate the appropriate authenticator
    plugins = plugins_disco.PluginsRegistry.find_all()
    config = configuration.NamespaceConfig(_AttrDict(renewalparams))
    # XXX: this loses type data (for example, the fact that key_size
    #      was an int, not a str)
    config.rsa_key_size = int(config.rsa_key_size)
    config.tls_sni_01_port = int(config.tls_sni_01_port)
    config.namespace.http01_port = int(config.namespace.http01_port)
    zope.component.provideUtility(config)
    try:
        authenticator = plugins[renewalparams["authenticator"]]
    except KeyError:
        # TODO: Notify user? (authenticator could not be found)
        return False
    authenticator = authenticator.init(config)

    authenticator.prepare()
    acc = account.AccountFileStorage(config).load(
        account_id=renewalparams["account"])

    le_client = client.Client(config, acc, authenticator, None)
    with open(cert.version("cert", old_version)) as f:
        sans = crypto_util.get_sans_from_cert(f.read())
    new_certr, new_chain, new_key, _ = le_client.obtain_certificate(sans)
    if new_chain:
        # XXX: Assumes that there was a key change.  We need logic
        #      for figuring out whether there was or not.  Probably
        #      best is to have obtain_certificate return None for
        #      new_key if the old key is to be used (since save_successor
        #      already understands this distinction!)
        return cert.save_successor(
            old_version,
            OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
                                            new_certr.body), new_key.pem,
            crypto_util.dump_pyopenssl_chain(new_chain))
        # TODO: Notify results
    else:
        # TODO: Notify negative results
        return False