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
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()
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)
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
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/"])
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()
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)
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)
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))
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()
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