1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-13 10:22:20 +03:00

Move CONFIG to CLI arguments

This commit is contained in:
Jakub Warmuz
2015-01-31 11:28:33 +00:00
parent 687541505b
commit b6de602b5b
11 changed files with 152 additions and 186 deletions

View File

@@ -1,81 +0,0 @@
"""Config for Let's Encrypt."""
import os.path
import zope.component
from letsencrypt.client import interfaces
zope.component.moduleProvides(interfaces.IConfig)
ACME_SERVER = "letsencrypt-demo.org:443"
"""CA hostname (and optionally :port).
If you create your own server... change this line
Note: the server certificate must be trusted in order to avoid
further modifications to the client."""
CONFIG_DIR = "/etc/letsencrypt/"
"""Configuration file directory for letsencrypt"""
WORK_DIR = "/var/lib/letsencrypt/"
"""Working directory for letsencrypt"""
BACKUP_DIR = os.path.join(WORK_DIR, "backups/")
"""Directory where configuration backups are stored"""
TEMP_CHECKPOINT_DIR = os.path.join(WORK_DIR, "temp_checkpoint/")
"""Directory where temp checkpoint is created"""
IN_PROGRESS_DIR = os.path.join(BACKUP_DIR, "IN_PROGRESS/")
"""Directory used before a permanent checkpoint is finalized"""
CERT_KEY_BACKUP = os.path.join(WORK_DIR, "keys-certs/")
"""Directory where all certificates/keys are stored. Used for easy revocation"""
REV_TOKENS_DIR = os.path.join(WORK_DIR, "revocation_tokens/")
"""Directory where all revocation tokens are saved."""
KEY_DIR = os.path.join(CONFIG_DIR, "keys/")
"""Keys storage."""
CERT_DIR = os.path.join(CONFIG_DIR, "certs/")
"""Certificate storage."""
LE_VHOST_EXT = "-le-ssl.conf"
"""Let's Encrypt SSL vhost configuration extension."""
CERT_PATH = os.path.join(CERT_DIR, "cert-letsencrypt.pem")
"""Let's Encrypt cert file."""
CHAIN_PATH = os.path.join(CERT_DIR, "chain-letsencrypt.pem")
"""Let's Encrypt chain file."""
RSA_KEY_SIZE = 2048
"""Key size"""
APACHE_CTL = "/usr/sbin/apache2ctl"
"""Path to the ``apache2ctl`` binary, used for ``configtest`` and
retrieving Apache2 version number."""
APACHE_ENMOD = "apache"
"""Path to the Apache ``a2enmod`` binary."""
APACHE_INIT_SCRIPT = "/etc/init.d/apache2"
"""Path to the Apache init script (used for server reload/restart)."""
APACHE_REWRITE_HTTPS_ARGS = [
"^.*$", "https://%{SERVER_NAME}%{REQUEST_URI}", "[L,R=permanent]"]
"""Apache rewrite rule arguments used for redirections to https vhost"""
APACHE_SERVER_ROOT = "/etc/apache2/"
"""Apache server root directory"""
APACHE_MOD_SSL_CONF = os.path.join(CONFIG_DIR, "options-ssl.conf")
"""Contains standard Apache SSL directives"""

View File

@@ -87,15 +87,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
self.config = config
server_root = self.config.APACHE_SERVER_ROOT
ssl_options = self.config.APACHE_MOD_SSL_CONF
server_root = self.config.apache_server_root
ssl_options = self.config.apache_mod_ssl_conf
if direc is None:
direc = {"backup": self.config.BACKUP_DIR,
"temp": self.config.TEMP_CHECKPOINT_DIR,
"progress": self.config.IN_PROGRESS_DIR,
"config": self.config.CONFIG_DIR,
"work": self.config.WORK_DIR}
direc = {"backup": self.config.backup_dir,
"temp": self.config.temp_checkpoint_dir,
"progress": self.config.in_progress_dir,
"config": self.config.config_dir,
"work": self.config.work_dir}
super(ApacheConfigurator, self).__init__(config, direc)
self.direc = direc
@@ -384,10 +384,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
is appropriately listening on port 443.
"""
if not mod_loaded("ssl_module", self.config.APACHE_CTL):
if not mod_loaded("ssl_module", self.config.apache_ctl):
logging.info("Loading mod_ssl into Apache Server")
enable_mod("ssl", self.config.APACHE_INIT_SCRIPT,
self.config.APACHE_ENMOD)
enable_mod("ssl", self.config.apache_init_script,
self.config.apache_enmod)
# Check for Listen 443
# Note: This could be made to also look for ip:443 combo
@@ -430,7 +430,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Makes an ssl_vhost version of a nonssl_vhost.
Duplicates vhost and adds default ssl options
New vhost will reside as (nonssl_vhost.path) + ``IConfig.LE_VHOST_EXT``
New vhost will reside as (nonssl_vhost.path) + ``IConfig.le_vhost_ext``
.. note:: This function saves the configuration
@@ -444,9 +444,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
avail_fp = nonssl_vhost.filep
# Get filepath of new ssl_vhost
if avail_fp.endswith(".conf"):
ssl_fp = avail_fp[:-(len(".conf"))] + self.config.LE_VHOST_EXT
ssl_fp = avail_fp[:-(len(".conf"))] + self.config.le_vhost_ext
else:
ssl_fp = avail_fp + self.config.LE_VHOST_EXT
ssl_fp = avail_fp + self.config.le_vhost_ext
# First register the creation so that it is properly removed if
# configuration is rolled back
@@ -566,9 +566,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:rtype: (bool, :class:`letsencrypt.client.apache.obj.VirtualHost`)
"""
if not mod_loaded("rewrite_module", self.config.APACHE_CTL):
enable_mod("rewrite", self.config.APACHE_INIT_SCRIPT,
self.config.APACHE_ENMOD)
if not mod_loaded("rewrite_module", self.config.apache_ctl):
enable_mod("rewrite", self.config.apache_init_script,
self.config.apache_enmod)
general_v = self._general_vhost(ssl_vhost)
if general_v is None:
@@ -593,7 +593,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Add directives to server
self.parser.add_dir(general_v.path, "RewriteEngine", "On")
self.parser.add_dir(general_v.path, "RewriteRule",
self.config.APACHE_REWRITE_HTTPS_ARGS)
constants.APACHE_REWRITE_HTTPS_ARGS)
self.save_notes += ('Redirecting host in %s to ssl vhost in %s\n' %
(general_v.filep, ssl_vhost.filep))
self.save()
@@ -632,10 +632,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
if not rewrite_path:
# "No existing redirection for virtualhost"
return False, -1
if len(rewrite_path) == len(self.config.APACHE_REWRITE_HTTPS_ARGS):
if len(rewrite_path) == len(constants.APACHE_REWRITE_HTTPS_ARGS):
for idx, match in enumerate(rewrite_path):
if (self.aug.get(match) !=
self.config.APACHE_REWRITE_HTTPS_ARGS[idx]):
constants.APACHE_REWRITE_HTTPS_ARGS[idx]):
# Not a letsencrypt https rewrite
return True, 2
# Existing letsencrypt https rewrite rule is in place
@@ -684,7 +684,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"LogLevel warn\n"
"</VirtualHost>\n"
% (servername, serveralias,
" ".join(self.config.APACHE_REWRITE_HTTPS_ARGS)))
" ".join(constants.APACHE_REWRITE_HTTPS_ARGS)))
# Write out the file
# This is the default name
@@ -886,7 +886,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:rtype: bool
"""
return apache_restart(self.config.APACHE_INIT_SCRIPT)
return apache_restart(self.config.apache_init_script)
def config_test(self): # pylint: disable=no-self-use
"""Check the configuration of Apache for errors.
@@ -897,7 +897,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
try:
proc = subprocess.Popen(
['sudo', self.config.APACHE_CTL, 'configtest'], # TODO: sudo?
['sudo', self.config.apache_ctl, 'configtest'], # TODO: sudo?
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
@@ -928,13 +928,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
try:
proc = subprocess.Popen(
[self.config.APACHE_CTL, '-v'],
[self.config.apache_ctl, '-v'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
text = proc.communicate()[0]
except (OSError, ValueError):
raise errors.LetsEncryptConfiguratorError(
"Unable to run %s -v" % self.config.APACHE_CTL)
"Unable to run %s -v" % self.config.apache_ctl)
regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE)
matches = regex.findall(text)

View File

@@ -30,9 +30,9 @@ class AugeasConfigurator(object):
"""
if not direc:
direc = {"backup": config.BACKUP_DIR,
"temp": config.TEMP_CHECKPOINT_DIR,
"progress": config.IN_PROGRESS_DIR}
direc = {"backup": config.backup_dir,
"temp": config.temp_checkpoint_dir,
"progress": config.in_progress_dir}
# Set Augeas flags to not save backup (we do it ourselves)
# Set Augeas to not load anything by default

View File

@@ -86,8 +86,8 @@ class Client(object):
:rtype: `tuple` of `str`
"""
cert_path = self.config.CERT_PATH
chain_path = self.config.CHAIN_PATH
cert_path = self.config.cert_path
chain_path = self.config.chain_path
if self.auth_handler is None:
logging.warning("Unable to obtain a certificate, because client "
@@ -103,7 +103,7 @@ class Client(object):
# Create CSR from names
if csr is None:
csr = init_csr(self.authkey, domains, self.config.CERT_DIR)
csr = init_csr(self.authkey, domains, self.config.cert_dir)
# Retrieve certificate
certificate_dict = self.acme_certificate(csr.data)
@@ -245,8 +245,8 @@ class Client(object):
:rtype: bool
"""
list_file = os.path.join(self.config.CERT_KEY_BACKUP, "LIST")
le_util.make_or_verify_dir(self.config.CERT_KEY_BACKUP, 0o700)
list_file = os.path.join(self.config.cert_key_backup, "LIST")
le_util.make_or_verify_dir(self.config.cert_key_backup, 0o700)
idx = 0
if encrypt:
@@ -271,11 +271,11 @@ class Client(object):
shutil.copy2(self.authkey.file,
os.path.join(
self.config.CERT_KEY_BACKUP,
self.config.cert_key_backup,
os.path.basename(self.authkey.file) + "_" + str(idx)))
shutil.copy2(cert_file,
os.path.join(
self.config.CERT_KEY_BACKUP,
self.config.cert_key_backup,
os.path.basename(cert_file) + "_" + str(idx)))
return True

View File

@@ -28,7 +28,7 @@ class ClientAuthenticator(object):
"""
self.rec_token = recovery_token.RecoveryToken(
server, config.REV_TOKEN_DIRS)
server, config.rev_token_dirs)
def get_chall_pref(self, unused_domain): # pylint: disable=no-self-use
"""Return list of challenge preferences."""

View File

@@ -9,6 +9,7 @@ challanges."""
NONCE_SIZE = 16
"""Size of nonce used in JWS objects (in bytes)."""
EXCLUSIVE_CHALLENGES = [frozenset(["dvsni", "simpleHttps"])]
"""Mutually exclusive challenges."""
@@ -20,6 +21,7 @@ CLIENT_CHALLENGES = frozenset(
["recoveryToken", "recoveryContact", "proofOfPossession"])
"""Challenges that are handled by the Let's Encrypt client."""
ENHANCEMENTS = ["redirect", "http-header", "ocsp-stapling", "spdy"]
"""List of possible :class:`letsencrypt.client.interfaces.IInstaller`
enhancements.
@@ -32,10 +34,16 @@ List of expected options parameters:
"""
APACHE_MOD_SSL_CONF = pkg_resources.resource_filename(
'letsencrypt.client.apache', 'options-ssl.conf')
"""Path to the Apache mod_ssl config file found in the Let's Encrypt
distribution."""
APACHE_REWRITE_HTTPS_ARGS = [
"^.*$", "https://%{SERVER_NAME}%{REQUEST_URI}", "[L,R=permanent]"]
"""Apache rewrite rule arguments used for redirections to https vhost"""
DVSNI_DOMAIN_SUFFIX = ".acme.invalid"
"""Suffix appended to domains in DVSNI validation."""

View File

@@ -23,9 +23,9 @@ class Reverter(object):
"""
if direc is None:
direc = {'backup': config.BACKUP_DIR,
'temp': config.TEMP_CHECKPOINT_DIR,
'progress': config.IN_PROGRESS_DIR}
direc = {'backup': config.backup_dir,
'temp': config.temp_checkpoint_dir,
'progress': config.in_progress_dir}
self.direc = direc
def revert_temporary_config(self):

View File

@@ -54,7 +54,7 @@ class Revoker(object):
def list_certs_keys(self):
"""List trusted Let's Encrypt certificates."""
list_file = os.path.join(self.config.CERT_KEY_BACKUP, "LIST")
list_file = os.path.join(self.config.cert_key_backup, "LIST")
certs = []
if not os.path.isfile(list_file):
@@ -75,9 +75,9 @@ class Revoker(object):
for row in csvreader:
cert = crypto_util.get_cert_info(row[1])
b_k = os.path.join(self.config.CERT_KEY_BACKUP,
b_k = os.path.join(self.config.cert_key_backup,
os.path.basename(row[2]) + "_" + row[0])
b_c = os.path.join(self.config.CERT_KEY_BACKUP,
b_c = os.path.join(self.config.cert_key_backup,
os.path.basename(row[1]) + "_" + row[0])
cert.update({
@@ -124,8 +124,8 @@ class Revoker(object):
:param dict cert: Cert dict used throughout revocation
"""
list_file = os.path.join(self.config.CERT_KEY_BACKUP, "LIST")
list_file2 = os.path.join(self.config.CERT_KEY_BACKUP, "LIST.tmp")
list_file = os.path.join(self.config.cert_key_backup, "LIST")
list_file2 = os.path.join(self.config.cert_key_backup, "LIST.tmp")
with open(list_file, 'rb') as orgfile:
csvreader = csv.reader(orgfile)

View File

@@ -65,9 +65,9 @@ def get_apache_configurator(
# This just states that the ssl module is already loaded
mock_popen().communicate.return_value = ("ssl_module", "")
config = configurator.ApacheConfigurator(
mock.MagicMock(APACHE_SERVER_ROOT=config_path,
APACHE_MOD_SSL_CONF=ssl_options,
LE_VHOST_EXT="-le-ssl.conf"),
mock.MagicMock(apache_server_root=config_path,
apache_mod_ssl_conf=ssl_options,
le_vhost_ext="-le-ssl.conf"),
{
"backup": backups,
"temp": os.path.join(work_dir, "temp_checkpoint"),

View File

@@ -392,9 +392,9 @@ class QuickInitReverterTest(unittest.TestCase):
from letsencrypt.client.reverter import Reverter
config = mock.MagicMock()
rev = Reverter(config)
self.assertEqual(rev.direc['backup'], config.BACKUP_DIR)
self.assertEqual(rev.direc['temp'], config.TEMP_CHECKPOINT_DIR)
self.assertEqual(rev.direc['progress'], config.IN_PROGRESS_DIR)
self.assertEqual(rev.direc['backup'], config.backup_dir)
self.assertEqual(rev.direc['temp'], config.temp_checkpoint_dir)
self.assertEqual(rev.direc['progress'], config.in_progress_dir)
def setup_work_direc():

View File

@@ -13,57 +13,95 @@ import zope.component
import zope.interface
import letsencrypt
from letsencrypt.client import CONFIG
from letsencrypt.client import client
from letsencrypt.client import display
from letsencrypt.client import errors
from letsencrypt.client import interfaces
from letsencrypt.client import log
def main(): # pylint: disable=too-many-statements,too-many-branches
"""Command line argument parsing and main script execution."""
def create_parser():
"""Create parser."""
parser = argparse.ArgumentParser(
description="letsencrypt client %s" % letsencrypt.__version__)
parser.add_argument("-d", "--domains", dest="domains", metavar="DOMAIN",
nargs="+")
parser.add_argument("-s", "--server", dest="server",
default=CONFIG.ACME_SERVER,
help="The ACME CA server. [%(default)s]")
parser.add_argument("-p", "--privkey", dest="privkey", type=read_file,
help="Path to the private key file for certificate "
"generation.")
parser.add_argument("-b", "--rollback", dest="rollback", type=int,
default=0, metavar="N",
help="Revert configuration N number of checkpoints.")
parser.add_argument("-B", "--keysize", dest="key_size", type=int,
default=CONFIG.RSA_KEY_SIZE, metavar="N",
help="RSA key shall be sized N bits. [%(default)d]")
parser.add_argument("-k", "--revoke", dest="revoke", action="store_true",
help="Revoke a certificate.")
parser.add_argument("-v", "--view-config-changes",
dest="view_config_changes",
action="store_true",
help="View checkpoints and associated configuration "
"changes.")
parser.add_argument("-r", "--redirect", dest="redirect",
action="store_const", const=True,
help="Automatically redirect all HTTP traffic to HTTPS "
"for the newly authenticated vhost.")
parser.add_argument("-n", "--no-redirect", dest="redirect",
action="store_const", const=False,
help="Skip the HTTPS redirect question, allowing both "
"HTTP and HTTPS.")
parser.add_argument("-e", "--agree-tos", dest="eula", action="store_true",
help="Skip the end user license agreement screen.")
parser.add_argument("-t", "--text", dest="use_curses", action="store_false",
help="Use the text output instead of the curses UI.")
parser.add_argument("--test", dest="test", action="store_true",
help="Run in test mode.")
add = parser.add_argument
add("-d", "--domains", metavar="DOMAIN", nargs="+")
add("-s", "--acme-server", "--server", default="letsencrypt-demo.org:443",
help="CA hostname (and optionally :port). The server certificate must "
"be trusted in order to avoid further modifications to the "
"client.")
add("-p", "--privkey", type=read_file,
help="Path to the private key file for certificate generation.")
add("-B", "--rsa-key-size", "--keysize", type=int, default=2048,
metavar="N", help="RSA key shall be sized N bits.")
add("-k", "--revoke", action="store_true", help="Revoke a certificate.")
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.")
add("-r", "--redirect", action="store_true",
help="Automatically redirect all HTTP traffic to HTTPS for the newly "
"authenticated vhost.")
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.")
add("--test", action="store_true", help="Run in test mode.")
# TODO: trailing slashes might be important! check and remove
add("--config-dir", default="/etc/letsencrypt/",
help="Configuration directory.")
add("--work-dir", default="/var/lib/letsencrypt/",
help="Working directory.")
add("--backup-dir", default="/var/lib/letsencrypt/backups/",
help="Configuration backups directory.")
add("--temp-checkpoint-dir",
default="/var/lib/letsencrypt/temp_checkpoint/",
help="Temporary checkpoint directory.")
add("--in-progress-dir",
default="/var/lib/letsencrypt/backups/IN_PROGRESS/",
help="Directory used before a permanent checkpoint is finalized")
add("--cert-key-backup", default="/var/lib/letsencrypt/keys-certs/",
help="Directory where all certificates and keys are stored. "
"Used for easy revocation.")
add("--rev-tokens-dir", default="/var/lib/letsencrypt/revocation_tokens/",
help="Directory where all revocation tokens are saved.")
add("--key-dir", default="/etc/letsencrypt/keys/", help="Keys storage.")
add("--cert-dir", default="/etc/letsencrypt/certs/",
help="Certificates storage.")
add("--le-vhost-ext", default="-le-ssl.conf",
help="SSL vhost configuration extension.")
add("--cert-path", default="/etc/letsencrypt/certs/cert-letsencrypt.pem",
help="Let's Encrypt certificate file.")
add("--chain-path", default="/etc/letsencrypt/certs/chain-letsencrypt.pem",
help="Let's Encrypt chain file.")
add("--apache-ctl", default="/usr/bin/apache2ctl",
help="Path to the 'apache2ctl' binary, used for 'configtest' and "
"retrieving Apache2 version number.")
add("--apache-enmod", default="a2enmod",
help="Path to the Apache 'a2enmod' binary.")
add("--apache-init-script", default="/etc/init.d/apache2",
help="Path to the Apache init script (used for server reload/restart).")
add("--apache-server-root", default="/etc/apache2",
help="Apache server root directory.")
add("--apache-mod-ssl-conf", default="/etc/letsencrypt/options-ssl.conf",
help="Contains standard Apache SSL directives.")
return parser
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 = parser.parse_args()
config = create_parser().parse_args()
zope.interface.directlyProvides(config, interfaces.IConfig)
# note: check is done after arg parsing as --help should work w/o root also.
if not os.geteuid() == 0:
@@ -74,32 +112,32 @@ def main(): # pylint: disable=too-many-statements,too-many-branches
# Set up logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if args.use_curses:
if config.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)
if config.view_config_changes:
client.view_config_changes(config)
sys.exit()
if args.revoke:
client.revoke(args.server, CONFIG)
if config.revoke:
client.revoke(config.acme_server, config)
sys.exit()
if args.rollback > 0:
client.rollback(args.rollback, CONFIG)
if config.rollback > 0:
client.rollback(config.rollback, config)
sys.exit()
if not args.eula:
if not config.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)
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: "
@@ -110,17 +148,18 @@ def main(): # pylint: disable=too-many-statements,too-many-branches
if interfaces.IAuthenticator.providedBy(installer): # pylint: disable=no-member
auth = installer
else:
auth = client.determine_authenticator(CONFIG)
auth = client.determine_authenticator(config)
domains = choose_names(installer) if args.domains is None else args.domains
if config.domains is None:
domains = choose_names(installer)
# Prepare for init of Client
if args.privkey is None:
privkey = client.init_key(args.key_size, CONFIG.KEY_DIR)
if config.privkey is None:
privkey = client.init_key(config.key_size, config.key_dir)
else:
privkey = client.Client.Key(args.privkey[0], args.privkey[1])
privkey = client.Client.Key(config.privkey[0], config.privkey[1])
acme = client.Client(args.server, privkey, auth, installer, CONFIG)
acme = client.Client(config.acme_server, privkey, auth, installer, config)
# Validate the key and csr
client.validate_key_csr(privkey)
@@ -134,7 +173,7 @@ def main(): # pylint: disable=too-many-statements,too-many-branches
if installer is not None and cert_file is not None:
acme.deploy_certificate(domains, privkey, cert_file, chain_file)
if installer is not None:
acme.enhance_config(domains, args.redirect)
acme.enhance_config(domains, config.redirect)
def display_eula():