diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index ecf6764f7..8cd8d9955 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -98,8 +98,9 @@ def run(args, config, plugins): return "Configurator could not be determined" acme, doms = _common_run(args, config, acc, authenticator, installer) - cert_key, cert_path, chain_path = acme.obtain_certificate(doms) - acme.deploy_certificate(doms, cert_key, cert_path, chain_path) + cert_key, act_cert_path, act_chain_path = acme.obtain_certificate( + doms, args.cert_path, args.chain_path) + acme.deploy_certificate(doms, cert_key, act_cert_path, act_chain_path) acme.enhance_config(doms, args.redirect) @@ -121,7 +122,7 @@ def auth(args, config, plugins): acme, doms = _common_run( args, config, acc, authenticator=authenticator, installer=installer) - acme.obtain_certificate(doms) + acme.obtain_certificate(doms, args.cert_path, args.chain_path) def install(args, config, plugins): @@ -135,21 +136,21 @@ def install(args, config, plugins): return "Installer could not be determined" acme, doms = _common_run( args, config, acc, authenticator=None, installer=installer) - assert args.cert_path is not None + assert args.cert_path is not None # required=True in the subparser acme.deploy_certificate(doms, acc.key, args.cert_path, args.chain_path) acme.enhance_config(doms, args.redirect) def revoke(args, unused_config, unused_plugins): """Revoke.""" - if args.rev_cert is None and args.rev_key is None: - return "At least one of --certificate or --key is required" + if args.cert_path is None and args.key_path is None: + return "At least one of --cert-path or --key-path is required" # This depends on the renewal config and cannot be completed yet. zope.component.getUtility(interfaces.IDisplay).notification( "Revocation is not available with the new Boulder server yet.") #client.revoke(args.installer, config, plugins, args.no_confirm, - # args.rev_cert, args.rev_key) + # args.cert_path, args.key_path) def rollback(args, config, plugins): @@ -248,13 +249,29 @@ def create_parser(plugins): subparser.set_defaults(func=func) return subparser - add_subparser("run", run) - add_subparser("auth", auth) - add_subparser("install", install) - parser_revoke = add_subparser("revoke", revoke) - parser_rollback = add_subparser("rollback", rollback) + parser_run = add_subparser("run", run) + parser_auth = add_subparser("auth", auth) add_subparser("config_changes", config_changes) + for subparser in (parser_run, parser_auth): + subparser.add_argument( + "--cert-path", default=flag_default("cert_path"), + help="Candidate path where a freshly issued certificate will " + "be saved to. If a file already exists at the provided " + "path, dirpath/0001_filename.ext will be attempted " + "(securely).") + subparser.add_argument( + "--chain-path", default=flag_default("chain_path"), + help="Candidate path (see --cert-path help) where an " + "accompanying certificate chain will be saved.") + + parser_install = add_subparser("install", install) + parser_install.add_argument( + "--cert-path", required=True, help="Path to a certificate that " + "is going to be installed.") + parser_install.add_argument( + "--chain-path", help="Accompanying path to a certificate chain.") + parser_plugins = add_subparser("plugins", plugins_cmd) parser_plugins.add_argument("--init", action="store_true") parser_plugins.add_argument("--prepare", action="store_true") @@ -287,13 +304,14 @@ def create_parser(plugins): help="Automatically redirect all HTTP traffic to HTTPS for the newly " "authenticated vhost.") + parser_revoke = add_subparser("revoke", revoke) parser_revoke.add_argument( - "--certificate", dest="rev_cert", type=read_file, metavar="CERT_PATH", - help="Revoke a specific certificate.") + "--cert-path", type=read_file, help="Revoke a specific certificate.") parser_revoke.add_argument( - "--key", dest="rev_key", type=read_file, metavar="KEY_PATH", + "--key-path", type=read_file, help="Revoke all certs generated by the provided authorized key.") + parser_rollback = add_subparser("rollback", rollback) parser_rollback.add_argument( "--checkpoints", type=int, metavar="N", default=flag_default("rollback_checkpoints"), @@ -323,10 +341,6 @@ def _paths_parser(parser): help=config_help("key_dir")) add("--cert-dir", default=flag_default("certs_dir"), help=config_help("cert_dir")) - add("--cert-path", default=flag_default("cert_path"), - help=config_help("cert_path")) - add("--chain-path", default=flag_default("chain_path"), - help=config_help("chain_path")) return parser diff --git a/letsencrypt/client.py b/letsencrypt/client.py index ae1667dfa..4362592f8 100644 --- a/letsencrypt/client.py +++ b/letsencrypt/client.py @@ -95,7 +95,7 @@ class Client(object): self.account.save() - def obtain_certificate(self, domains, csr=None): + def obtain_certificate(self, domains, cert_path, chain_path, csr=None): """Obtains a certificate from the ACME server. :meth:`.register` must be called before :meth:`.obtain_certificate` @@ -104,6 +104,9 @@ class Client(object): :param set domains: domains to get a certificate + :param str cert_path: Candidate path to a certificate. + :param str chain_path: Candidate path to a certificate chain. + :param csr: CSR must contain requested domains, the key used to generate this CSR can be different than self.authkey :type csr: :class:`CSR` @@ -137,13 +140,13 @@ class Client(object): authzr) # Save Certificate - cert_path, chain_path = self.save_certificate( - certr, self.config.cert_path, self.config.chain_path) + act_cert_path, act_chain_path = self.save_certificate( + certr, cert_path, chain_path) revoker.Revoker.store_cert_key( - cert_path, self.account.key.file, self.config) + act_cert_path, self.account.key.file, self.config) - return cert_key, cert_path, chain_path + return cert_key, act_cert_path, act_chain_path def save_certificate(self, certr, cert_path, chain_path): # pylint: disable=no-self-use @@ -152,8 +155,8 @@ class Client(object): :param certr: ACME "certificate" resource. :type certr: :class:`acme.messages.Certificate` - :param str cert_path: Path to attempt to save the cert file - :param str chain_path: Path to attempt to save the chain file + :param str cert_path: Candidate path to a certificate. + :param str chain_path: Candidate path to a certificate chain. :returns: cert_path, chain_path (absolute paths to the actual files) :rtype: `tuple` of `str` diff --git a/letsencrypt/constants.py b/letsencrypt/constants.py index 9ff0b128c..29365797a 100644 --- a/letsencrypt/constants.py +++ b/letsencrypt/constants.py @@ -1,5 +1,6 @@ """Let's Encrypt constants.""" import logging +import os from acme import challenges @@ -8,19 +9,22 @@ SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins" """Setuptools entry point group name for plugins.""" +_CLI_DEFAULT_CONFIG_DIR = "/etc/letsencrypt" +_CLI_DEFAULT_WORK_DIR = "/var/lib/letsencrypt" +_CLI_DEFAULT_CERT_DIR = os.path.join(_CLI_DEFAULT_CONFIG_DIR, "certs") CLI_DEFAULTS = dict( config_files=["/etc/letsencrypt/cli.ini"], verbose_count=-(logging.WARNING / 10), server="https://www.letsencrypt-demo.org/acme/new-reg", rsa_key_size=2048, rollback_checkpoints=0, - config_dir="/etc/letsencrypt", - work_dir="/var/lib/letsencrypt", - backup_dir="/var/lib/letsencrypt/backups", - key_dir="/etc/letsencrypt/keys", - certs_dir="/etc/letsencrypt/certs", - cert_path="/etc/letsencrypt/certs/cert-letsencrypt.pem", - chain_path="/etc/letsencrypt/certs/chain-letsencrypt.pem", + config_dir=_CLI_DEFAULT_CONFIG_DIR, + work_dir=_CLI_DEFAULT_CONFIG_DIR, + backup_dir=os.path.join(_CLI_DEFAULT_WORK_DIR, "backups"), + key_dir=os.path.join(_CLI_DEFAULT_CONFIG_DIR, "keys"), + certs_dir=_CLI_DEFAULT_CERT_DIR, + cert_path=os.path.join(_CLI_DEFAULT_CERT_DIR, "cert-letsencrypt.pem"), + chain_path=os.path.join(_CLI_DEFAULT_CERT_DIR, "chain-letsencrypt.pem"), test_mode=False, ) """Defaults for CLI flags and `.IConfig` attributes.""" diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 609b9410a..68d442227 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -170,13 +170,9 @@ class IConfig(zope.interface.Interface): "Directory where all account keys are stored.") rec_token_dir = zope.interface.Attribute( "Directory where all recovery tokens are saved.") - key_dir = zope.interface.Attribute("Keys storage.") - cert_dir = zope.interface.Attribute("Certificates storage.") - le_vhost_ext = zope.interface.Attribute( - "SSL vhost configuration extension.") - cert_path = zope.interface.Attribute("Let's Encrypt certificate file path.") - chain_path = zope.interface.Attribute("Let's Encrypt chain file path.") + key_dir = zope.interface.Attribute("Keys storage.") + cert_dir = zope.interface.Attribute("Certificates and CSRs storage.") test_mode = zope.interface.Attribute( "Test mode. Disables certificate verification.")