2015-01-28 19:18:40 -05:00
|
|
|
"""Parse command line and call the appropriate functions.
|
|
|
|
|
|
2015-02-11 18:38:59 -05:00
|
|
|
.. todo:: Sanity check all input. Be sure to avoid shell code etc...
|
2015-01-28 19:18:40 -05:00
|
|
|
|
|
|
|
|
"""
|
2014-11-26 21:10:04 -05:00
|
|
|
import argparse
|
2014-12-10 05:32:22 -05:00
|
|
|
import logging
|
2014-11-19 07:28:02 -05:00
|
|
|
import os
|
2015-03-11 03:51:06 -04:00
|
|
|
import pkg_resources
|
2014-11-19 07:28:02 -05:00
|
|
|
import sys
|
|
|
|
|
|
2015-01-31 06:37:24 -05:00
|
|
|
import confargparse
|
2014-12-17 06:02:15 -05:00
|
|
|
import zope.component
|
|
|
|
|
|
2015-01-29 08:58:20 -05:00
|
|
|
import letsencrypt
|
2015-01-31 06:28:33 -05:00
|
|
|
|
2015-02-03 06:22:04 -05:00
|
|
|
from letsencrypt.client import configuration
|
2014-11-19 07:28:02 -05:00
|
|
|
from letsencrypt.client import client
|
2015-02-24 14:53:57 -05:00
|
|
|
from letsencrypt.client import errors
|
2015-02-02 21:11:48 -05:00
|
|
|
from letsencrypt.client import interfaces
|
|
|
|
|
from letsencrypt.client import le_util
|
2014-12-10 05:32:22 -05:00
|
|
|
from letsencrypt.client import log
|
2015-02-20 01:30:11 -05:00
|
|
|
from letsencrypt.client import standalone_authenticator as standalone
|
2015-02-19 22:33:54 -05:00
|
|
|
from letsencrypt.client.apache import configurator
|
2015-02-18 07:01:49 -05:00
|
|
|
from letsencrypt.client.display import util as display_util
|
2015-02-23 07:26:43 -05:00
|
|
|
from letsencrypt.client.display import ops as display_ops
|
2014-11-19 07:28:02 -05:00
|
|
|
|
2014-11-26 21:10:04 -05:00
|
|
|
|
2015-01-31 06:28:33 -05:00
|
|
|
def create_parser():
|
|
|
|
|
"""Create parser."""
|
2015-01-31 06:37:24 -05:00
|
|
|
parser = confargparse.ConfArgParser(
|
2015-01-29 08:58:20 -05:00
|
|
|
description="letsencrypt client %s" % letsencrypt.__version__)
|
2014-11-26 21:30:42 -05:00
|
|
|
|
2015-01-31 06:28:33 -05:00
|
|
|
add = parser.add_argument
|
2015-02-03 07:13:57 -05:00
|
|
|
config_help = lambda name: interfaces.IConfig[name].__doc__
|
2015-01-31 06:28:33 -05:00
|
|
|
|
|
|
|
|
add("-d", "--domains", metavar="DOMAIN", nargs="+")
|
2015-02-09 17:39:49 -05:00
|
|
|
add("-s", "--server", default="letsencrypt-demo.org:443",
|
|
|
|
|
help=config_help("server"))
|
2015-01-31 06:28:33 -05:00
|
|
|
|
2015-02-16 02:17:53 -05:00
|
|
|
add("-k", "--authkey", type=read_file,
|
|
|
|
|
help="Path to the authorized key file")
|
2015-02-03 07:13:57 -05:00
|
|
|
add("-B", "--rsa-key-size", type=int, default=2048, metavar="N",
|
|
|
|
|
help=config_help("rsa_key_size"))
|
2015-01-31 06:28:33 -05:00
|
|
|
|
2015-02-16 02:17:53 -05:00
|
|
|
add("-R", "--revoke", action="store_true",
|
|
|
|
|
help="Revoke a certificate from a menu.")
|
|
|
|
|
add("--revoke-certificate", dest="rev_cert", type=read_file,
|
|
|
|
|
help="Revoke a specific certificate.")
|
|
|
|
|
add("--revoke-key", dest="rev_key", type=read_file,
|
|
|
|
|
help="Revoke all certs generated by the provided authorized key.")
|
|
|
|
|
|
2015-01-31 06:28:33 -05:00
|
|
|
add("-b", "--rollback", type=int, default=0, metavar="N",
|
|
|
|
|
help="Revert configuration N number of checkpoints.")
|
|
|
|
|
add("-v", "--view-config-changes", action="store_true",
|
|
|
|
|
help="View checkpoints and associated configuration changes.")
|
2015-02-09 19:39:08 -05:00
|
|
|
|
|
|
|
|
# TODO: resolve - assumes binary logic while client.py assumes ternary.
|
|
|
|
|
add("-r", "--redirect", action="store_true",
|
2015-01-31 06:28:33 -05:00
|
|
|
help="Automatically redirect all HTTP traffic to HTTPS for the newly "
|
|
|
|
|
"authenticated vhost.")
|
|
|
|
|
|
2015-02-16 02:17:53 -05:00
|
|
|
add("--no-confirm", dest="no_confirm", action="store_true",
|
|
|
|
|
help="Turn off confirmation screens, currently used for --revoke")
|
|
|
|
|
|
2015-01-31 06:28:33 -05:00
|
|
|
add("-e", "--agree-tos", dest="eula", action="store_true",
|
|
|
|
|
help="Skip the end user license agreement screen.")
|
|
|
|
|
add("-t", "--text", dest="use_curses", action="store_false",
|
|
|
|
|
help="Use the text output instead of the curses UI.")
|
2015-02-09 17:53:51 -05:00
|
|
|
|
2015-02-02 16:11:59 -05:00
|
|
|
add("--config-dir", default="/etc/letsencrypt",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("config_dir"))
|
2015-02-02 16:11:59 -05:00
|
|
|
add("--work-dir", default="/var/lib/letsencrypt",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("work_dir"))
|
2015-02-02 16:11:59 -05:00
|
|
|
add("--backup-dir", default="/var/lib/letsencrypt/backups",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("backup_dir"))
|
|
|
|
|
add("--key-dir", default="/etc/letsencrypt/keys",
|
|
|
|
|
help=config_help("key_dir"))
|
2015-02-02 16:11:59 -05:00
|
|
|
add("--cert-dir", default="/etc/letsencrypt/certs",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("cert_dir"))
|
2015-01-31 06:28:33 -05:00
|
|
|
|
|
|
|
|
add("--le-vhost-ext", default="-le-ssl.conf",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("le_vhost_ext"))
|
2015-01-31 06:28:33 -05:00
|
|
|
add("--cert-path", default="/etc/letsencrypt/certs/cert-letsencrypt.pem",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("cert_path"))
|
2015-01-31 06:28:33 -05:00
|
|
|
add("--chain-path", default="/etc/letsencrypt/certs/chain-letsencrypt.pem",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("chain_path"))
|
2015-01-31 06:28:33 -05:00
|
|
|
|
|
|
|
|
add("--apache-server-root", default="/etc/apache2",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("apache_server_root"))
|
2015-01-31 06:28:33 -05:00
|
|
|
add("--apache-mod-ssl-conf", default="/etc/letsencrypt/options-ssl.conf",
|
2015-02-03 07:13:57 -05:00
|
|
|
help=config_help("apache_mod_ssl_conf"))
|
|
|
|
|
add("--apache-ctl", default="apache2ctl", help=config_help("apache_ctl"))
|
|
|
|
|
add("--apache-enmod", default="a2enmod", help=config_help("apache_enmod"))
|
|
|
|
|
add("--apache-init-script", default="/etc/init.d/apache2",
|
|
|
|
|
help=config_help("apache_init_script"))
|
2015-01-31 06:28:33 -05:00
|
|
|
|
|
|
|
|
return parser
|
|
|
|
|
|
2015-02-03 06:22:04 -05:00
|
|
|
|
2015-02-24 14:53:57 -05:00
|
|
|
def main(): # pylint: disable=too-many-branches, too-many-statements
|
2015-01-31 06:28:33 -05:00
|
|
|
"""Command line argument parsing and main script execution."""
|
2015-01-25 19:21:15 -05:00
|
|
|
# note: arg parser internally handles --help (and exits afterwards)
|
2015-02-03 06:22:04 -05:00
|
|
|
args = create_parser().parse_args()
|
|
|
|
|
config = configuration.NamespaceConfig(args)
|
2014-11-26 21:10:04 -05:00
|
|
|
|
2015-01-25 19:21:15 -05:00
|
|
|
# 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))
|
2014-11-26 21:10:04 -05:00
|
|
|
|
2014-12-10 05:32:22 -05:00
|
|
|
# Set up logging
|
|
|
|
|
logger = logging.getLogger()
|
2014-12-21 06:37:29 -05:00
|
|
|
logger.setLevel(logging.INFO)
|
2014-12-10 05:32:22 -05:00
|
|
|
if args.use_curses:
|
|
|
|
|
logger.addHandler(log.DialogHandler())
|
2015-02-08 03:46:16 -05:00
|
|
|
displayer = display_util.NcursesDisplay()
|
2014-11-19 07:28:02 -05:00
|
|
|
else:
|
2015-02-08 03:46:16 -05:00
|
|
|
displayer = display_util.FileDisplay(sys.stdout)
|
2015-02-19 07:13:39 -05:00
|
|
|
|
2014-12-17 06:02:15 -05:00
|
|
|
zope.component.provideUtility(displayer)
|
2014-11-19 07:28:02 -05:00
|
|
|
|
2015-01-19 06:15:31 -05:00
|
|
|
if args.view_config_changes:
|
2015-01-31 06:28:33 -05:00
|
|
|
client.view_config_changes(config)
|
2014-12-21 06:37:29 -05:00
|
|
|
sys.exit()
|
|
|
|
|
|
2015-02-23 07:26:43 -05:00
|
|
|
if args.revoke or args.rev_cert is not None or args.rev_key is not None:
|
2015-02-16 02:17:53 -05:00
|
|
|
client.revoke(config, args.no_confirm, args.rev_cert, args.rev_key)
|
2014-11-26 21:10:04 -05:00
|
|
|
sys.exit()
|
2014-11-19 07:28:02 -05:00
|
|
|
|
2015-01-19 06:15:31 -05:00
|
|
|
if args.rollback > 0:
|
2015-02-03 07:13:57 -05:00
|
|
|
client.rollback(args.rollback, config)
|
2014-11-26 21:10:04 -05:00
|
|
|
sys.exit()
|
2014-11-19 07:28:02 -05:00
|
|
|
|
2015-01-15 04:43:54 -05:00
|
|
|
if not args.eula:
|
|
|
|
|
display_eula()
|
|
|
|
|
|
2015-02-19 22:33:54 -05:00
|
|
|
all_auths = [
|
2015-02-23 07:26:43 -05:00
|
|
|
configurator.ApacheConfigurator(config),
|
|
|
|
|
standalone.StandaloneAuthenticator(),
|
2015-02-19 22:33:54 -05:00
|
|
|
]
|
2015-02-24 14:53:57 -05:00
|
|
|
try:
|
|
|
|
|
auth = client.determine_authenticator(all_auths)
|
|
|
|
|
except errors.LetsEncryptClientError:
|
|
|
|
|
logging.critical("No authentication mechanisms were found on your "
|
|
|
|
|
"system.")
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
2015-02-19 07:13:39 -05:00
|
|
|
if auth is None:
|
2015-02-24 14:53:57 -05:00
|
|
|
sys.exit(0)
|
2015-01-19 06:15:31 -05:00
|
|
|
|
2014-12-21 06:37:29 -05:00
|
|
|
# Use the same object if possible
|
2015-02-09 03:12:43 -05:00
|
|
|
if interfaces.IInstaller.providedBy(auth): # pylint: disable=no-member
|
|
|
|
|
installer = auth
|
2014-12-21 06:37:29 -05:00
|
|
|
else:
|
2015-02-19 07:13:39 -05:00
|
|
|
# This is simple and avoids confusion right now.
|
|
|
|
|
installer = None
|
2014-11-25 03:43:18 -05:00
|
|
|
|
2015-02-23 07:26:43 -05:00
|
|
|
if args.domains is None:
|
|
|
|
|
doms = display_ops.choose_names(installer)
|
|
|
|
|
else:
|
|
|
|
|
doms = args.domains
|
2014-12-17 09:27:21 -05:00
|
|
|
|
2015-02-24 14:53:57 -05:00
|
|
|
if not doms:
|
|
|
|
|
sys.exit(0)
|
|
|
|
|
|
2014-11-29 19:05:18 -05:00
|
|
|
# Prepare for init of Client
|
2015-02-19 07:13:39 -05:00
|
|
|
if args.authkey is None:
|
|
|
|
|
authkey = client.init_key(args.rsa_key_size, config.key_dir)
|
2014-11-30 05:31:44 -05:00
|
|
|
else:
|
2015-02-19 07:13:39 -05:00
|
|
|
authkey = le_util.Key(args.authkey[0], args.authkey[1])
|
2014-11-29 19:05:18 -05:00
|
|
|
|
2015-02-19 07:13:39 -05:00
|
|
|
acme = client.Client(config, authkey, auth, installer)
|
2014-12-17 09:27:21 -05:00
|
|
|
|
2014-12-21 06:37:29 -05:00
|
|
|
# Validate the key and csr
|
2015-02-19 07:13:39 -05:00
|
|
|
client.validate_key_csr(authkey)
|
2014-12-21 06:37:29 -05:00
|
|
|
|
2015-01-24 05:15:23 -05:00
|
|
|
# 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.
|
2015-02-09 03:12:43 -05:00
|
|
|
cert_file = None
|
2015-01-24 05:15:23 -05:00
|
|
|
if auth is not None:
|
2015-02-09 05:47:45 -05:00
|
|
|
cert_file, chain_file = acme.obtain_certificate(doms)
|
2015-01-24 05:15:23 -05:00
|
|
|
if installer is not None and cert_file is not None:
|
2015-02-19 07:13:39 -05:00
|
|
|
acme.deploy_certificate(doms, authkey, cert_file, chain_file)
|
2015-01-24 05:15:23 -05:00
|
|
|
if installer is not None:
|
2015-02-09 05:47:45 -05:00
|
|
|
acme.enhance_config(doms, args.redirect)
|
2014-12-17 09:27:21 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def display_eula():
|
|
|
|
|
"""Displays the end user agreement."""
|
2015-03-11 03:51:06 -04:00
|
|
|
eula = pkg_resources.resource_string("letsencrypt", "EULA")
|
|
|
|
|
if not zope.component.getUtility(interfaces.IDisplay).yesno(
|
|
|
|
|
eula, "Agree", "Cancel"):
|
|
|
|
|
sys.exit(0)
|
2014-12-17 09:27:21 -05:00
|
|
|
|
|
|
|
|
|
2014-11-28 15:48:04 -05:00
|
|
|
def read_file(filename):
|
|
|
|
|
"""Returns the given file's contents with universal new line support.
|
2014-11-28 11:41:03 -05:00
|
|
|
|
2014-11-29 21:07:52 -05:00
|
|
|
:param str filename: Filename
|
2014-11-28 11:41:03 -05:00
|
|
|
|
2014-11-30 07:10:23 -05:00
|
|
|
:returns: A tuple of filename and its contents
|
|
|
|
|
:rtype: tuple
|
2014-11-28 11:41:03 -05:00
|
|
|
|
2014-11-29 11:15:56 -05:00
|
|
|
:raises argparse.ArgumentTypeError: File does not exist or is not readable.
|
2014-11-28 11:41:03 -05:00
|
|
|
|
2014-11-29 11:15:56 -05:00
|
|
|
"""
|
|
|
|
|
try:
|
2015-02-02 20:28:34 -05:00
|
|
|
return filename, open(filename, "rU").read()
|
2014-11-29 11:15:56 -05:00
|
|
|
except IOError as exc:
|
|
|
|
|
raise argparse.ArgumentTypeError(exc.strerror)
|
2014-11-28 11:41:03 -05:00
|
|
|
|
|
|
|
|
|
2014-11-26 21:30:42 -05:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|