1
0
mirror of https://github.com/certbot/certbot.git synced 2026-01-26 07:41:33 +03:00

Merge pull request #117 from letsencrypt/configurator_tests

Configurator tests
This commit is contained in:
James Kasten
2014-12-10 00:43:36 -08:00
47 changed files with 1654 additions and 248 deletions

View File

@@ -1,3 +1,4 @@
recursive-include letsencrypt *.json
recursive-include letsencrypt *.sh
recursive-include letsencrypt *.conf
recursive include letsencrypt/client/tests/testdata *

View File

@@ -10,6 +10,7 @@ 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."""
# Directories
SERVER_ROOT = "/etc/apache2/"
"""Apache server root directory"""
@@ -39,6 +40,7 @@ KEY_DIR = os.path.join(SERVER_ROOT, "ssl/")
CERT_DIR = os.path.join(SERVER_ROOT, "certs/")
"""Certificate storage"""
# Files and extensions
OPTIONS_SSL_CONF = os.path.join(CONFIG_DIR, "options-ssl.conf")
"""Contains standard Apache SSL directives"""
@@ -48,18 +50,6 @@ LE_VHOST_EXT = "-le-ssl.conf"
APACHE_CHALLENGE_CONF = os.path.join(CONFIG_DIR, "le_dvsni_cert_challenge.conf")
"""Temporary file for challenge virtual hosts"""
S_SIZE = 32
"""Byte size of S"""
NONCE_SIZE = 16
"""byte size of Nonce"""
RSA_KEY_SIZE = 2048
"""Key size"""
DIFFICULTY = 23
"""bits of hashcash to generate"""
CERT_PATH = CERT_DIR + "cert-letsencrypt.pem"
"""Let's Encrypt cert file."""
@@ -69,6 +59,7 @@ CHAIN_PATH = CERT_DIR + "chain-letsencrypt.pem"
INVALID_EXT = ".acme.invalid"
"""Invalid Extension"""
# Challenge Information
CHALLENGE_PREFERENCES = ["dvsni", "recoveryToken"]
"""Challenge Preferences Dict for currently supported challenges"""
@@ -78,6 +69,25 @@ EXCLUSIVE_CHALLENGES = [frozenset(["dvsni", "simpleHttps"])]
CONFIG_CHALLENGES = frozenset(["dvsni", "simpleHttps"])
"""These are challenges that must be solved by a Configurator object"""
# Challenge Constants
S_SIZE = 32
"""Byte size of S"""
NONCE_SIZE = 16
"""byte size of Nonce"""
# Key Sizes
RSA_KEY_SIZE = 2048
"""Key size"""
# Config Optimizations
REWRITE_HTTPS_ARGS = [
"^.*$", "https://%{SERVER_NAME}%{REQUEST_URI}", "[L,R=permanent]"]
"""Rewrite rule arguments used for redirections to https vhost"""
# Apache Interaction
APACHE_CTL = "/usr/sbin/apache2ctl"
"""Command used for configtest and version number."""
APACHE2 = "/etc/init.d/apache2"
"""Command used for reload and restart."""

View File

@@ -7,7 +7,7 @@ import jsonschema
from letsencrypt.client import crypto_util
from letsencrypt.client import le_util
# pylint: disable=no-member
SCHEMATA = dict([
(schema, json.load(open(pkg_resources.resource_filename(
__name__, "schemata/%s.json" % schema)))) for schema in [

View File

@@ -7,7 +7,6 @@ import shutil
import socket
import subprocess
import sys
import time
from Crypto import Random
@@ -47,24 +46,20 @@ from letsencrypt.client import logger
class VH(object):
"""Represents an Apache Virtualhost.
:ivar str file: filename path of VH
:ivar str filep: file path of VH
:ivar str path: Augeas path to virtual host
:ivar list addrs: Virtual Host addresses (:class:`list` of :class:`str`)
:ivar list names: Server names/aliases of vhost
(:class:`list` of :class:`str`)
:ivar bool ssl: SSLEngine on in vhost
:ivar bool enabled: Virtual host is enabled
"""
def __init__(self, filename, path, addrs, ssl, enabled):
def __init__(self, filep, path, addrs, ssl, enabled):
"""Initialize a VH."""
self.file = filename
self.filep = filep
self.path = path
self.addrs = addrs
self.names = []
@@ -85,9 +80,18 @@ class VH(object):
"addrs: %s\n"
"names: %s\n"
"ssl: %s\n"
"enabled: %s" % (self.file, self.path, self.addrs,
"enabled: %s" % (self.filep, self.path, self.addrs,
self.names, self.ssl, self.enabled))
def __eq__(self, other):
if isinstance(other, self.__class__):
return (self.filep == other.filep and self.path == other.path and
set(self.addrs) == set(other.addrs) and
set(self.names) == set(other.names) and
self.ssl == other.ssl and self.enabled == other.enabled)
return False
class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""Apache configurator.
@@ -98,37 +102,49 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
This class was originally developed for Apache 2.2 and has not seen a
an overhaul to include proper setup of new Apache configurations.
The biggest changes have been the IncludeOptional directive, the
deprecation of the NameVirtualHost directive, and the name change of
mod_ssl.c to ssl_module. Although these changes
have not been implemented yet, they will be shortly.
I have implemented most of the changes... the missing ones are
mod_ssl.c vs ssl_mod, and I need to account for configuration variables.
That being said, this class can still adequately configure most typical
Apache 2.4 servers as the deprecated NameVirtualHost has no effect
and the typical directories are parsed by the Augeas configuration
parser automatically.
.. todo:: Add support for config file variables Define rootDir /var/www/
The API of this class will change in the coming weeks as the exact
needs of client's are clarified with the new and developing protocol.
This class will eventually derive from a generic Configurator class
so that other Configurators (like Nginx) can be developed and interoperate
with the client.
:ivar str server_root: Path to Apache root directory
:ivar dict location: Path to various files associated
with the configuration
:ivar float version: version of Apache
:ivar str user_config_file: Path to the user's configuration file
:ivar list vhosts: All vhosts found in the configuration
(:class:`list` of :class:`VH`)
:ivar dict assoc: Mapping between domains and vhosts
"""
def __init__(self, server_root=CONFIG.SERVER_ROOT):
"""Initialize an Apache Configurator."""
super(ApacheConfigurator, self).__init__()
def __init__(self, server_root=CONFIG.SERVER_ROOT, direc=None,
ssl_options=CONFIG.OPTIONS_SSL_CONF, version=None):
"""Initialize an Apache Configurator.
:param str server_root: the apache server root directory
:param dict direc: locations of various config directories
(used mostly for unittesting)
:param str ssl_options: path of options-ssl.conf
(used mostly for unittesting)
:param tup version: version of Apache as a tuple (2, 4, 7)
(used mostly for unittesting)
"""
if direc is None:
direc = {"backup": CONFIG.BACKUP_DIR,
"temp": CONFIG.TEMP_CHECKPOINT_DIR,
"progress": CONFIG.IN_PROGRESS_DIR,
"config": CONFIG.CONFIG_DIR,
"work": CONFIG.WORK_DIR}
super(ApacheConfigurator, self).__init__(direc)
self.server_root = server_root
@@ -138,28 +154,37 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# vhosts
self.recovery_routine()
# Verify that all directories and files exist with proper permissions
if os.geteuid() == 0:
self.verify_setup()
# Find configuration root and make sure augeas can parse it.
self.location = self._set_locations(ssl_options)
self._parse_file(self.location["root"])
# Must also attempt to parse sites-available or equivalent
# Sites-available is not included naturally in configuration
self._parse_file(os.path.join(self.server_root, "sites-available/*"))
# Set Version
self.version = self.get_version() if version is None else version
# Check for errors in parsing files with Augeas
self.check_parsing_errors("httpd.aug")
# This problem has been fixed in Augeas 1.0
self.standardize_excl()
# Determine user's main config file
self.user_config_file = self._set_user_config_file()
# Get all of the available vhosts
self.vhosts = self.get_virtual_hosts()
# Add name_server association dict
self.assoc = dict()
# Verify that all directories and files exist with proper permissions
verify_setup()
# Enable mod_ssl if it isn't already enabled
# This is Let's Encrypt... we enable mod_ssl on initialization :)
# TODO: attempt to make the check faster... this enable should
# be asynchronous as it shouldn't be that time sensitive
# on initialization
if not check_ssl_loaded():
logger.info("Loading mod_ssl into Apache Server")
enable_mod("ssl")
self._prepare_server_https()
# Note: initialization doesn't check to see if the config is correct
# by Apache's standards. This should be done by the client (client.py)
@@ -214,7 +239,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# Presumably break here so that the virtualhost is not modified
return False
logger.info("Deploying Certificate to VirtualHost %s" % vhost.file)
logger.info("Deploying Certificate to VirtualHost %s" % vhost.filep)
self.aug.set(path["cert_file"][0], cert)
self.aug.set(path["cert_key"][0], key)
@@ -225,7 +250,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self.aug.set(path["cert_chain"][0], cert_chain)
self.save_notes += ("Changed vhost at %s with addresses of %s\n" %
(vhost.file, vhost.addrs))
(vhost.filep, vhost.addrs))
self.save_notes += "\tSSLCertificateFile %s\n" % cert
self.save_notes += "\tSSLCertificateKeyFile %s\n" % key
if cert_chain:
@@ -322,6 +347,40 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
return all_names
def _set_locations(self, ssl_options):
"""Set default location for directives.
Locations are given as file_paths
.. todo:: Make sure that files are included
"""
root = self._find_config_root()
default = self._set_user_config_file()
temp = os.path.join(self.server_root, "ports.conf")
if os.path.isfile(temp):
listen = temp
name = temp
else:
listen = default
name = default
return {"root": root, "default": default, "listen": listen,
"name": name, "ssl_options": ssl_options}
def _find_config_root(self):
"""Find the Apache Configuration Root file."""
location = ["apache2.conf", "httpd.conf"]
for name in location:
if os.path.isfile(os.path.join(self.server_root, name)):
return os.path.join(self.server_root, name)
raise errors.LetsEncryptConfiguratorError(
"Could not find configuration root")
def _set_user_config_file(self, filename=''):
"""Set the appropriate user configuration file
@@ -357,6 +416,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
case_i('ServerName'),
host.path,
case_i('ServerAlias'))))
for name in name_match:
args = self.aug.match(name + "/*")
for arg in args:
@@ -377,8 +437,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
addrs.append(self.aug.get(arg))
is_ssl = False
if len(self.find_directive(
case_i("SSLEngine"), case_i("on"), path)) > 0:
if self.find_directive(
case_i("SSLEngine"), case_i("on"), path):
is_ssl = True
filename = get_file_path(path)
@@ -400,15 +460,19 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
("/files%ssites-available//*[label()=~regexp('%s')]" %
(self.server_root, case_i('VirtualHost'))))
vhs = []
for path in paths:
vhs.append(self._create_vhost(path))
return vhs
# pylint: disable=anomalous-backslash-in-string
def is_name_vhost(self, target_addr):
"""Returns if vhost is a name based vhost
Checks if addr has a NameVirtualHost directive in the Apache config
NameVirtualHost was deprecated in Apache 2.4 as all VirtualHosts are
now NameVirtualHosts. If version is earlier than 2.4, check if addr
has a NameVirtualHost directive in the Apache config
:param str addr: vhost address ie. \*:443
@@ -416,49 +480,28 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:rtype: bool
"""
# search for NameVirtualHost directive for ip_addr
# check httpd.conf, ports.conf,
# note ip_addr can be FQDN although Apache does not recommend it
paths = self.find_directive(case_i("NameVirtualHost"), None)
name_vh = []
for path in paths:
name_vh.append(self.aug.get(path))
# Mixed and matched wildcard NameVirtualHost with VirtualHost
# behavior is undefined. Make sure that an exact match exists
# Check for exact match
for addr in name_vh:
if addr == target_addr:
return True
return False
# search for NameVirtualHost directive for ip_addr
# note ip_addr can be FQDN although Apache does not recommend it
return (self.version >= (2, 4) or
self.find_directive(
case_i("NameVirtualHost"), case_i(target_addr)))
def add_name_vhost(self, addr):
"""Adds NameVirtualHost directive for given address.
Directive is added to ports.conf unless the file doesn't exist
It is added to httpd.conf as a backup
:param str addr: Address that will be added as NameVirtualHost directive
"""
aug_file_path = "/files%sports.conf" % self.server_root
self.add_dir_to_ifmodssl(aug_file_path, "NameVirtualHost", addr)
path = self._add_dir_to_ifmodssl(
get_aug_path(self.location["name"]), "NameVirtualHost", addr)
# TODO: Check to see if len(find_dir) can just be if find_dir()
if len(self.find_directive(
case_i("NameVirtualHost"), case_i(addr))) == 0:
logger.warn("ports.conf is not included in your Apache config...")
logger.warn("Adding NameVirtualHost directive to httpd.conf")
self.save_notes += "Setting %s to be NameBasedVirtualHost\n" % addr
self.save_notes += "\tDirective added to %s\n" % path
self.add_dir_to_ifmodssl("/files%shttpd.conf" % self.server_root,
"NameVirtualHost",
addr)
self.save_notes += 'Setting %s to be NameBasedVirtualHost\n' % addr
def add_dir_to_ifmodssl(self, aug_conf_path, directive, val):
def _add_dir_to_ifmodssl(self, aug_conf_path, directive, val):
"""Adds directive and value to IfMod ssl block.
Adds given directive and value along configuration path within
@@ -472,39 +515,45 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
# TODO: Add error checking code... does the path given even exist?
# Does it throw exceptions?
if_mod_path = self.get_ifmod(aug_conf_path, "mod_ssl.c")
if_mod_path = self._get_ifmod(aug_conf_path, "mod_ssl.c")
# IfModule can have only one valid argument, so append after
self.aug.insert(if_mod_path + "arg", "directive", False)
nvh_path = if_mod_path + "directive[1]"
self.aug.set(nvh_path, directive)
self.aug.set(nvh_path + "/arg", val)
def _prepare_server_https(self):
"""Prepare the server for HTTPS.
Make sure that the ssl_module is loaded and that the server
is appropriately listening on port 443.
"""
if not check_ssl_loaded():
logger.info("Loading mod_ssl into Apache Server")
enable_mod("ssl")
# Check for Listen 443
# Note: This could be made to also look for ip:443 combo
# TODO: Need to search only open directives and IfMod mod_ssl.c
if len(self.find_directive(case_i("Listen"), "443")) == 0:
logger.debug("No Listen 443 directive found")
logger.debug("Setting the Apache Server to Listen on port 443")
path = self._add_dir_to_ifmodssl(
get_aug_path(self.location["listen"]), "Listen", "443")
self.save_notes += "Added Listen 443 directive to %s\n" % path
def make_server_sni_ready(self, vhost, default_addr="*:443"):
"""Checks to see if the server is ready for SNI challenges.
.. todo:: This should largely depend on the version of Apache
:param vhost: VHost to check SNI compatibility
:type vhost: :class:`VH`
:param str default_addr: TODO - investigate function further
"""
# Check if mod_ssl is loaded
if not check_ssl_loaded():
logger.error("Please load the SSL module with Apache")
return False
# Check for Listen 443
# TODO: This could be made to also look for ip:443 combo
# TODO: Need to search only open directives and IfMod mod_ssl.c
if len(self.find_directive(case_i("Listen"), "443")) == 0:
logger.debug("No Listen 443 directive found")
logger.debug("Setting the Apache Server to Listen on port 443")
self.add_dir_to_ifmodssl("/files%sports.conf" % self.server_root,
"Listen", "443")
self.save_notes += "Added Listen 443 directive to ports.conf\n"
if self.version >= (2, 4):
return
# Check for NameVirtualHost
# First see if any of the vhost addresses is a _default_ addr
for addr in vhost.addrs:
@@ -515,7 +564,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"%s to be name based vhosts" % default_addr))
self.add_name_vhost(default_addr)
return True
# No default addresses... so set each one individually
for addr in vhost.addrs:
if not self.is_name_vhost(addr):
@@ -523,9 +571,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"to be a name based virtual host" % addr))
self.add_name_vhost(addr)
return True
def get_ifmod(self, aug_conf_path, mod):
def _get_ifmod(self, aug_conf_path, mod):
"""Returns the path to <IfMod mod> and creates one if it doesn't exist.
:param str aug_conf_path: Augeas configuration path
@@ -533,25 +579,25 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
if_mods = self.aug.match(("%s/IfModule/*[self::arg='%s']" %
(aug_conf_path, mod)))
(aug_conf_path, mod)))
if len(if_mods) == 0:
self.aug.set("%s/IfModule[last() + 1]" % aug_conf_path, "")
self.aug.set("%s/IfModule[last()]/arg" % aug_conf_path, mod)
if_mods = self.aug.match(("%s/IfModule/*[self::arg='%s']" %
(aug_conf_path, mod)))
(aug_conf_path, mod)))
# Strip off "arg" at end of first ifmod path
return if_mods[0][:len(if_mods[0]) - 3]
def add_dir(self, aug_conf_path, directive, arg):
"""Appends directive to the end fo the file given by aug_conf_path.
Note: Not added to AugeasConfigurator because it may depend on the lens
.. note:: Not added to AugeasConfigurator because it may depend
on the lens
:param str aug_conf_path: Augeas configuration path to add directive
:param str directive: Directive to add
:param str arg: Value of the directive. ie. Listen 443, 443 is arg
"""
self.aug.set(aug_conf_path + "/directive[last() + 1]", directive)
if type(arg) is not list:
@@ -588,7 +634,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"""
# Cannot place member variable in the definition of the function so...
if not start:
start = "/files%sapache2.conf" % self.server_root
start = get_aug_path(self.location["root"])
# Debug code
# print "find_dir:", directive, "arg:", arg, " | Looking in:", start
@@ -611,19 +657,24 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"[self::arg=~regexp('%s')]" %
(start, directive, arg)))
incl_regex = "(%s)|(%s)" % (case_i('Include'),
case_i('IncludeOptional'))
includes = self.aug.match(("%s//* [self::directive=~regexp('%s')]/* "
"[label()='arg']" %
(start, case_i('Include'))))
"[label()='arg']" % (start, incl_regex)))
# for inc in includes:
# print inc, self.aug.get(inc)
for include in includes:
# start[6:] to strip off /files
matches.extend(self.find_directive(
directive, arg, self.get_include_path(strip_dir(start[6:]),
self.aug.get(include))))
directive, arg, self._get_include_path(strip_dir(start[6:]),
self.aug.get(include))))
return matches
def get_include_path(self, cur_dir, arg):
def _get_include_path(self, cur_dir, arg):
"""Converts an Apache Include directive into Augeas path.
Converts an Apache Include directive argument into an Augeas
@@ -689,8 +740,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
# If the include is a directory, just return the directory as a file
if arg.endswith("/"):
return "/files" + arg[:len(arg)-1]
return "/files"+arg
return get_aug_path(arg[:len(arg)-1])
return get_aug_path(arg)
def make_vhost_ssl(self, nonssl_vhost):
"""Makes an ssl_vhost version of a nonssl_vhost.
@@ -705,7 +756,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
:rtype: :class:`VH`
"""
avail_fp = nonssl_vhost.file
avail_fp = nonssl_vhost.filep
# Copy file
if avail_fp.endswith(".conf"):
ssl_fp = avail_fp[:-(len(".conf"))] + CONFIG.LE_VHOST_EXT
@@ -743,8 +794,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
addr_match % (avail_fp, case_i('VirtualHost')))
for i in range(len(avail_addr_p)):
avail_old_arg = self.aug.get(avail_addr_p[i])
ssl_old_arg = self.aug.get(ssl_addr_p[i])
avail_old_arg = str(self.aug.get(avail_addr_p[i]))
ssl_old_arg = str(self.aug.get(ssl_addr_p[i]))
avail_tup = avail_old_arg.partition(":")
ssl_tup = ssl_old_arg.partition(":")
avail_new_addr = avail_tup[0] + ":80"
@@ -765,7 +816,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
"/etc/ssl/certs/ssl-cert-snakeoil.pem")
self.add_dir(vh_p[0], "SSLCertificateKeyFile",
"/etc/ssl/private/ssl-cert-snakeoil.key")
self.add_dir(vh_p[0], "Include", CONFIG.OPTIONS_SSL_CONF)
self.add_dir(vh_p[0], "Include", self.location["ssl_options"])
# Log actions and create save notes
logger.info("Created an SSL vhost at %s" % ssl_fp)
@@ -835,7 +886,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator):
self.add_dir(general_v.path,
"RewriteRule", CONFIG.REWRITE_HTTPS_ARGS)
self.save_notes += ('Redirecting host in %s to ssl vhost in %s\n' %
(general_v.file, ssl_vhost.file))
(general_v.filep, ssl_vhost.filep))
self.save()
return True, general_v
@@ -946,13 +997,13 @@ LogLevel warn \n\
self.aug.load()
# Make a new vhost data structure and add it to the lists
new_fp = self.server_root + "sites-available/" + redirect_filename
new_vhost = self._create_vhost("/files" + new_fp)
new_vhost = self._create_vhost(get_aug_path(new_fp))
self.vhosts.append(new_vhost)
# Finally create documentation for the change
self.save_notes += ('Created a port 80 vhost, %s, for redirection to '
'ssl vhost %s\n' %
(new_vhost.file, ssl_vhost.file))
(new_vhost.filep, ssl_vhost.filep))
return True, new_vhost
@@ -1071,7 +1122,7 @@ LogLevel warn \n\
# Can be removed once find directive can return ordered results
if len(cert_path) != 1 or len(key_path) != 1:
logger.error(("Too many cert or key directives in vhost "
"%s" % vhost.file))
"%s" % vhost.filep))
sys.exit(40)
cert = os.path.abspath(self.aug.get(cert_path[0]))
@@ -1112,17 +1163,17 @@ LogLevel warn \n\
:rtype: bool
"""
if self.is_site_enabled(vhost.file):
if self.is_site_enabled(vhost.filep):
return True
if "/sites-available/" in vhost.file:
if "/sites-available/" in vhost.filep:
enabled_path = ("%ssites-enabled/%s" %
(self.server_root, os.path.basename(vhost.file)))
(self.server_root, os.path.basename(vhost.filep)))
self.register_file_creation(False, enabled_path)
os.symlink(vhost.file, enabled_path)
os.symlink(vhost.filep, enabled_path)
vhost.enabled = True
logger.info("Enabling available site: %s" % vhost.file)
self.save_notes += 'Enabled site %s\n' % vhost.file
logger.info("Enabling available site: %s" % vhost.filep)
self.save_notes += 'Enabled site %s\n' % vhost.filep
return True
return False
@@ -1170,13 +1221,6 @@ LogLevel warn \n\
self._add_httpd_transform(file_path)
self.aug.load()
def save_apache_config(self):
"""Backup complete Apache config. Not currently used."""
# Not currently used
# Should be safe because it is a protected directory
shutil.copytree(self.server_root,
"%sapache2-%s" % (CONFIG.BACKUP_DIR, str(time.time())))
def standardize_excl(self):
"""Standardize the excl arguments for the Httpd lens in Augeas.
@@ -1216,7 +1260,7 @@ LogLevel warn \n\
:rtype: bool
"""
return apache_restart(quiet)
return apache_restart()
def _add_httpd_transform(self, incl):
"""Add a transform to Augeas.
@@ -1257,6 +1301,50 @@ LogLevel warn \n\
return True
def get_version(self): # pylint: disable=no-self-use
"""Return version of Apache Server.
Version is returned as tuple. (ie. 2.4.7 = (2, 4, 7))
:returns: version
:rtype: tuple
:raises errors.LetsEncryptConfiguratorError:
Unable to find Apache version
"""
try:
proc = subprocess.Popen(
[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" % CONFIG.APACHE_CTL)
regex = re.compile(r"Apache/([0-9\.]*)", re.IGNORECASE)
matches = regex.findall(text)
if len(matches) != 1:
raise errors.LetsEncryptConfiguratorError(
"Unable to find Apache version")
return tuple([int(i) for i in matches[0].split('.')])
def verify_setup(self):
"""Verify the setup to ensure safe operating environment.
Make sure that files/directories are setup with appropriate permissions
Aim for defensive coding... make sure all input files
have permissions of root
"""
uid = os.geteuid()
le_util.make_or_verify_dir(self.direc["config"], 0o755, uid)
le_util.make_or_verify_dir(self.direc["work"], 0o755, uid)
le_util.make_or_verify_dir(self.direc["backup"], 0o755, uid)
###########################################################################
# Challenges Section
###########################################################################
@@ -1312,8 +1400,7 @@ LogLevel warn \n\
return None
# TODO - @jdkasten review this code to make sure it makes sense
if not self.make_server_sni_ready(vhost, default_addr):
return None
self.make_server_sni_ready(vhost, default_addr)
for addr in vhost.addrs:
if "_default_" in addr:
@@ -1370,6 +1457,7 @@ LogLevel warn \n\
# SHOWING A NICE ERROR MESSAGE ABOUT THE PROBLEM
# Check to make sure options-ssl.conf is installed
# pylint: disable=no-member
if not os.path.isfile(CONFIG.OPTIONS_SSL_CONF):
dist_conf = pkg_resources.resource_filename(
__name__, os.path.basename(CONFIG.OPTIONS_SSL_CONF))
@@ -1378,11 +1466,11 @@ LogLevel warn \n\
# TODO: Use ip address of existing vhost instead of relying on FQDN
config_text = "<IfModule mod_ssl.c> \n"
for idx, lis in enumerate(ll_addrs):
config_text += get_config_text(
config_text += self.get_config_text(
list_sni_tuple[idx][2], lis, dvsni_key.file)
config_text += "</IfModule> \n"
self.dvsni_conf_include_check(self.user_config_file)
self.dvsni_conf_include_check(self.location["default"])
self.register_file_creation(True, CONFIG.APACHE_CHALLENGE_CONF)
with open(CONFIG.APACHE_CHALLENGE_CONF, 'w') as new_conf:
@@ -1400,13 +1488,13 @@ LogLevel warn \n\
if len(self.find_directive(
case_i("Include"), CONFIG.APACHE_CHALLENGE_CONF)) == 0:
# print "Including challenge virtual host(s)"
self.add_dir("/files" + main_config,
self.add_dir(get_aug_path(main_config),
"Include", CONFIG.APACHE_CHALLENGE_CONF)
def dvsni_create_chall_cert(self, name, ext, nonce, dvsni_key):
"""Creates DVSNI challenge certifiate.
Certificate created at dvsni_get_cert_file(nonce)
Certificate created at self.dvsni_get_cert_file(nonce)
:param str nonce: hex form of nonce
@@ -1414,14 +1502,50 @@ LogLevel warn \n\
:type dvsni_key: `client.Client.Key`
"""
self.register_file_creation(True, dvsni_get_cert_file(nonce))
self.register_file_creation(True, self.dvsni_get_cert_file(nonce))
cert_pem = crypto_util.make_ss_cert(
dvsni_key.pem, [nonce + CONFIG.INVALID_EXT, name, ext])
with open(dvsni_get_cert_file(nonce), 'w') as chall_cert_file:
with open(self.dvsni_get_cert_file(nonce), 'w') as chall_cert_file:
chall_cert_file.write(cert_pem)
def get_config_text(self, nonce, ip_addrs, dvsni_key_file):
"""Chocolate virtual server configuration text
:param str nonce: hex form of nonce
:param str ip_addrs: addresses of challenged domain
:param str dvsni_key_file: Path to key file
:returns: virtual host configuration text
:rtype: str
"""
return ("<VirtualHost " + " ".join(ip_addrs) + "> \n"
"ServerName " + nonce + CONFIG.INVALID_EXT + " \n"
"UseCanonicalName on \n"
"SSLStrictSNIVHostCheck on \n"
"\n"
"LimitRequestBody 1048576 \n"
"\n"
"Include " + self.location["ssl_options"] + " \n"
"SSLCertificateFile " + self.dvsni_get_cert_file(nonce) + " \n"
"SSLCertificateKeyFile " + dvsni_key_file + " \n"
"\n"
"DocumentRoot " + self.direc["config"] + "challenge_page/ \n"
"</VirtualHost> \n\n")
def dvsni_get_cert_file(self, nonce):
"""Returns standardized name for challenge certificate.
:param str nonce: hex form of nonce
:returns: certificate file name
:rtype: str
"""
return self.direc["work"] + nonce + ".crt"
def enable_mod(mod_name):
"""Enables module in Apache.
@@ -1437,7 +1561,7 @@ def enable_mod(mod_name):
stdout=open("/dev/null", 'w'),
stderr=open("/dev/null", 'w'))
# Hopefully this waits for output
subprocess.check_call(["sudo", "/etc/init.d/apache2", "restart"],
subprocess.check_call(["sudo", CONFIG.APACHE2, "restart"],
stdout=open("/dev/null", 'w'),
stderr=open("/dev/null", 'w'))
except (OSError, subprocess.CalledProcessError) as err:
@@ -1460,12 +1584,13 @@ def check_ssl_loaded():
try:
# p=subprocess.check_output(['sudo', '/usr/sbin/apache2ctl', '-M'],
# stderr=open("/dev/null", 'w'))
proc = subprocess.Popen(['sudo', '/usr/sbin/apache2ctl', '-M'],
proc = subprocess.Popen([CONFIG.APACHE_CTL, '-M'],
stdout=subprocess.PIPE,
stderr=open(
"/dev/null", 'w')).communicate()[0]
except (OSError, ValueError):
logger.error("Error accessing apache2ctl for loaded modules!")
logger.error(
"Error accessing %s for loaded modules!" % CONFIG.APACHE_CTL)
logger.error("This may be caused by an Apache Configuration Error")
return False
@@ -1474,7 +1599,7 @@ def check_ssl_loaded():
return False
def apache_restart(quiet=False):
def apache_restart():
"""Restarts the Apache Server.
.. todo:: Try to use reload instead. (This caused timing problems before)
@@ -1482,7 +1607,7 @@ def apache_restart(quiet=False):
"""
try:
proc = subprocess.Popen(['/etc/init.d/apache2', 'restart'],
proc = subprocess.Popen([CONFIG.APACHE2, 'restart'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
text = proc.communicate()
@@ -1502,19 +1627,6 @@ def apache_restart(quiet=False):
return True
def verify_setup():
"""Verify the setup to ensure safe operating environment.
Make sure that files/directories are setup with appropriate permissions
Aim for defensive coding... make sure all input files
have permissions of root
"""
le_util.make_or_verify_dir(CONFIG.CONFIG_DIR, 0o755)
le_util.make_or_verify_dir(CONFIG.WORK_DIR, 0o755)
le_util.make_or_verify_dir(CONFIG.BACKUP_DIR, 0o755)
def case_i(string):
"""Returns case insensitive regex.
@@ -1559,6 +1671,15 @@ def get_file_path(vhost_path):
return avail_fp
def get_aug_path(file_path):
"""Return augeas path for full filepath.
:param str file_path: Full filepath
"""
return "/files%s" % file_path
def strip_dir(path):
"""Returns directory of file path.
@@ -1578,51 +1699,14 @@ def strip_dir(path):
return ""
def dvsni_get_cert_file(nonce):
"""Returns standardized name for challenge certificate.
:param str nonce: hex form of nonce
:returns: certificate file name
:rtype: str
"""
return CONFIG.WORK_DIR + nonce + ".crt"
def get_config_text(nonce, ip_addrs, dvsni_key_file):
"""Chocolate virtual server configuration text
:param str nonce: hex form of nonce
:param str ip_addrs: addresses of challenged domain
:param str dvsni_key_file: Path to key file
:returns: virtual host configuration text
:rtype: str
"""
return ("<VirtualHost " + " ".join(ip_addrs) + "> \n"
"ServerName " + nonce + CONFIG.INVALID_EXT + " \n"
"UseCanonicalName on \n"
"SSLStrictSNIVHostCheck on \n"
"\n"
"LimitRequestBody 1048576 \n"
"\n"
"Include " + CONFIG.OPTIONS_SSL_CONF + " \n"
"SSLCertificateFile " + dvsni_get_cert_file(nonce) + " \n"
"SSLCertificateKeyFile " + dvsni_key_file + " \n"
"\n"
"DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n"
"</VirtualHost> \n\n")
def dvsni_gen_ext(dvsni_r, dvsni_s):
"""Generates z extension to be placed in certificate extension.
:param bytearray dvsni_r: DVSNI r value
:param bytearray dvsni_s: DVSNI s value
result: returns z + CONFIG.INVALID_EXT
:returns: z + CONFIG.INVALID_EXT
:rtype: str
"""
z_base = hashlib.new('sha256')
@@ -1640,7 +1724,7 @@ def main():
logger.setLogLevel(logger.DEBUG)
# for v in config.vhosts:
# print v.file
# print v.filep
# print v.addrs
# for name in v.names:
# print name
@@ -1678,7 +1762,7 @@ def main():
# for vh in config.vhosts:
# if not vh.addrs:
# print vh.names
# print vh.file
# print vh.filep
# if vh.addrs[0] == "23.20.47.131:80":
# print "Here we go"
# ssl_vh = config.make_vhost_ssl(vh)

View File

@@ -17,10 +17,29 @@ class AugeasConfigurator(configurator.Configurator):
.. todo:: Fix generic exception handling.
:ivar aug: Augeas object
:type aug: :class:`augeas.Augeas`
:ivar str save_notes: Human-readable configuration change notes
:ivar dict direc: dictionary containing save directory paths
"""
def __init__(self):
def __init__(self, direc=None):
"""Initialize Augeas Configurator.
:param dict direc: location of save directories
(used mostly for testing)
"""
super(AugeasConfigurator, self).__init__()
if not direc:
direc = {"backup": CONFIG.BACKUP_DIR,
"temp": CONFIG.TEMP_CHECKPOINT_DIR,
"progress": CONFIG.IN_PROGRESS_DIR}
self.direc = direc
# TODO: this instantiation can be optimized to only load
# relevant files - I believe -> NO_MODL_AUTOLOAD
# Set Augeas flags to save backup
@@ -68,7 +87,7 @@ class AugeasConfigurator(configurator.Configurator):
try:
# This is a noop save
self.aug.save()
except:
except (RuntimeError, IOError):
# Check for the root of save problems
new_errs = self.aug.match("/augeas//error")
# logger.error("During Save - " + mod_conf)
@@ -105,12 +124,12 @@ class AugeasConfigurator(configurator.Configurator):
# Create Checkpoint
if temporary:
self.add_to_checkpoint(CONFIG.TEMP_CHECKPOINT_DIR, save_files)
self.add_to_checkpoint(self.direc["temp"], save_files)
else:
self.add_to_checkpoint(CONFIG.IN_PROGRESS_DIR, save_files)
self.add_to_checkpoint(self.direc["progress"], save_files)
if title and not temporary and os.path.isdir(CONFIG.IN_PROGRESS_DIR):
success = self._finalize_checkpoint(CONFIG.IN_PROGRESS_DIR, title)
if title and not temporary and os.path.isdir(self.direc["progress"]):
success = self._finalize_checkpoint(self.direc["progress"], title)
if not success:
# This should never happen
# This will be hopefully be cleaned up on the recovery
@@ -130,12 +149,12 @@ class AugeasConfigurator(configurator.Configurator):
for all saves with temporary=True
"""
if os.path.isdir(CONFIG.TEMP_CHECKPOINT_DIR):
result = self._recover_checkpoint(CONFIG.TEMP_CHECKPOINT_DIR)
if os.path.isdir(self.direc["temp"]):
result = self._recover_checkpoint(self.direc["temp"])
if result != 0:
# We have a partial or incomplete recovery
logger.fatal("Incomplete or failed recovery for "
"%s" % CONFIG.TEMP_CHECKPOINT_DIR)
"%s" % self.direc["temp"])
sys.exit(67)
# Remember to reload Augeas
self.aug.load()
@@ -148,22 +167,22 @@ class AugeasConfigurator(configurator.Configurator):
"""
try:
rollback = int(rollback)
except:
except ValueError:
logger.error("Rollback argument must be a positive integer")
# Sanity check input
if rollback < 1:
logger.error("Rollback argument must be a positive integer")
return
backups = os.listdir(CONFIG.BACKUP_DIR)
backups = os.listdir(self.direc["backup"])
backups.sort()
if len(backups) < rollback:
logger.error(("Unable to rollback %d checkpoints, only "
"%d exist") % (rollback, len(backups)))
"%d exist") % (rollback, len(backups)))
while rollback > 0 and backups:
cp_dir = CONFIG.BACKUP_DIR + backups.pop()
cp_dir = self.direc["backup"] + backups.pop()
result = self._recover_checkpoint(cp_dir)
if result != 0:
logger.fatal("Failed to load checkpoint during rollback")
@@ -182,7 +201,7 @@ class AugeasConfigurator(configurator.Configurator):
called.
"""
backups = os.listdir(CONFIG.BACKUP_DIR)
backups = os.listdir(self.direc["backup"])
backups.sort(reverse=True)
if not backups:
@@ -193,31 +212,29 @@ class AugeasConfigurator(configurator.Configurator):
try:
for bkup in backups:
float(bkup)
except:
assert False, "Invalid files in %s" % CONFIG.BACKUP_DIR
except ValueError:
assert False, "Invalid files in %s" % self.direc["backup"]
for bkup in backups:
print time.ctime(float(bkup))
with open(
CONFIG.BACKUP_DIR + bkup + "/CHANGES_SINCE") as changes_fd:
cur_dir = self.direc["backup"] + bkup
with open(os.path.join(cur_dir, "CHANGES_SINCE")) as changes_fd:
print changes_fd.read()
print "Affected files:"
with open(
CONFIG.BACKUP_DIR + bkup + "/FILEPATHS") as paths_fd:
with open(os.path.join(cur_dir, "FILEPATHS")) as paths_fd:
filepaths = paths_fd.read().splitlines()
for path in filepaths:
print " %s" % path
try:
with open(
CONFIG.BACKUP_DIR + bkup + "/NEW_FILES") as new_fd:
with open(os.path.join(cur_dir, "NEW_FILES")) as new_fd:
print "New Configuration Files:"
filepaths = new_fd.read().splitlines()
for path in filepaths:
print " %s" % path
except:
pass
except (IOError, OSError) as exc:
print exc
print ""
def add_to_checkpoint(self, cp_dir, save_files):
@@ -227,7 +244,7 @@ class AugeasConfigurator(configurator.Configurator):
:param set save_files: set of files to save
"""
le_util.make_or_verify_dir(cp_dir, 0o755)
le_util.make_or_verify_dir(cp_dir, 0o755, os.geteuid())
existing_filepaths = []
op_fd = None
@@ -275,8 +292,8 @@ class AugeasConfigurator(configurator.Configurator):
shutil.copy2(os.path.join(
cp_dir,
os.path.basename(path) + '_' + str(idx)),
path)
except:
path)
except (IOError, OSError):
# This file is required in all checkpoints.
logger.error("Unable to recover files from %s" % cp_dir)
return 1
@@ -286,7 +303,7 @@ class AugeasConfigurator(configurator.Configurator):
try:
shutil.rmtree(cp_dir)
except:
except OSError:
logger.error("Unable to remove directory: %s" % cp_dir)
return -1
@@ -301,7 +318,7 @@ class AugeasConfigurator(configurator.Configurator):
:rtype: bool, str
"""
temp_path = "%sFILEPATHS" % CONFIG.TEMP_CHECKPOINT_DIR
temp_path = "%sFILEPATHS" % self.direc["temp"]
if os.path.isfile(temp_path):
with open(temp_path, 'r') as protected_fd:
protected_files = protected_fd.read().splitlines()
@@ -327,22 +344,22 @@ class AugeasConfigurator(configurator.Configurator):
"""
if temporary:
cp_dir = CONFIG.TEMP_CHECKPOINT_DIR
cp_dir = self.direc["temp"]
else:
cp_dir = CONFIG.IN_PROGRESS_DIR
cp_dir = self.direc["progress"]
le_util.make_or_verify_dir(cp_dir)
try:
with open(os.path.join(cp_dir, "NEW_FILES"), 'a') as new_fd:
for file_path in files:
new_fd.write("%s\n" % file_path)
except:
except (IOError, OSError):
logger.error("ERROR: Unable to register file creation")
def recovery_routine(self):
"""Revert all previously modified files.
First, any changes found in CONFIG.TEMP_CHECKPOINT_DIR are removed,
First, any changes found in self.direc["temp"] are removed,
then IN_PROGRESS changes are removed The order is important.
IN_PROGRESS is unable to add files that are already added by a TEMP
change. Thus TEMP must be rolled back first because that will be the
@@ -350,14 +367,14 @@ class AugeasConfigurator(configurator.Configurator):
"""
self.revert_challenge_config()
if os.path.isdir(CONFIG.IN_PROGRESS_DIR):
result = self._recover_checkpoint(CONFIG.IN_PROGRESS_DIR)
if os.path.isdir(self.direc["progress"]):
result = self._recover_checkpoint(self.direc["progress"])
if result != 0:
# We have a partial or incomplete recovery
# Not as egregious
# TODO: Additional tests? recovery
logger.fatal("Incomplete or failed recovery for %s" %
CONFIG.IN_PROGRESS_DIR)
self.direc["progress"])
sys.exit(68)
# Need to reload configuration after these changes take effect
@@ -390,7 +407,7 @@ class AugeasConfigurator(configurator.Configurator):
"File: %s - Could not be found to be deleted\n"
"Program was probably shut down unexpectedly, "
"in which case this is not a problem") % path)
except IOError:
except (IOError, OSError):
logger.fatal(
"Unable to remove filepaths contained within %s" % file_list)
sys.exit(41)
@@ -411,7 +428,7 @@ class AugeasConfigurator(configurator.Configurator):
:rtype: bool
"""
final_dir = os.path.join(CONFIG.BACKUP_DIR, str(time.time()))
final_dir = os.path.join(self.direc["backup"], str(time.time()))
changes_since_path = os.path.join(cp_dir, "CHANGES_SINCE")
changes_since_tmp_path = os.path.join(cp_dir, "CHANGES_SINCE.tmp")
@@ -423,12 +440,12 @@ class AugeasConfigurator(configurator.Configurator):
shutil.move(changes_since_tmp_path, changes_since_path)
except:
except (IOError, OSError):
logger.error("Unable to finalize checkpoint - adding title")
return False
try:
os.rename(cp_dir, final_dir)
except:
except OSError:
logger.error("Unable to finalize checkpoint, %s -> %s" %
(cp_dir, final_dir))
return False

View File

@@ -418,7 +418,7 @@ class Client(object):
cert_chain_abspath)
# Enable any vhost that was issued to, but not enabled
if not host.enabled:
logger.info("Enabling Site " + host.file)
logger.info("Enabling Site " + host.filep)
self.config.enable_site(host)
# sites may have been enabled / final cleanup
@@ -564,7 +564,8 @@ class Client(object):
"""
for ssl_vh in vhost:
success, redirect_vhost = self.config.enable_redirect(ssl_vh)
logger.info("\nRedirect vhost: " + redirect_vhost.file +
# pylint: disable=maybe-no-member
logger.info("\nRedirect vhost: " + redirect_vhost.filep +
" - " + str(success))
# If successful, make sure redirect site is enabled
if success:

View File

@@ -3,7 +3,7 @@
.. note:: This challenge has not been implemented into the project yet
"""
import dialog
import display
from letsencrypt.client import challenge
@@ -20,7 +20,7 @@ class RecoveryToken(challenge.Challenge):
self.token = ""
def perform(self, quiet=True):
cancel, self.token = dialog.generic_input(
cancel, self.token = display.generic_input(
"Please Input Recovery Token: ")
return cancel != 1

View File

@@ -0,0 +1 @@
"""Let's Encrypt Tests"""

View File

@@ -0,0 +1,310 @@
"""A series of unit tests for the Apache Configurator."""
import mock
import os
import pkg_resources
import re
import shutil
import sys
import tempfile
import unittest
from letsencrypt.client import apache_configurator
from letsencrypt.client import CONFIG
from letsencrypt.client import display
from letsencrypt.client import errors
from letsencrypt.client import logger
# pylint: disable=no-member
UBUNTU_CONFIGS = pkg_resources.resource_filename(
"letsencrypt.client.tests", "testdata/debian_apache_2_4")
TEMP_DIR = ""
CONFIG_DIR = ""
WORK_DIR = ""
# pylint: disable=invalid-name
def setUpModule():
"""Run once before all unittests."""
global TEMP_DIR, CONFIG_DIR, WORK_DIR
logger.setLogger(logger.FileLogger(sys.stdout))
logger.setLogLevel(logger.INFO)
display.set_display(display.NcursesDisplay())
TEMP_DIR = tempfile.mkdtemp("temp")
CONFIG_DIR = tempfile.mkdtemp("config")
WORK_DIR = tempfile.mkdtemp("work")
shutil.copytree(UBUNTU_CONFIGS,
os.path.join(TEMP_DIR, "debian_apache_2_4"), symlinks=True)
TEMP_DIR = os.path.join(TEMP_DIR, "debian_apache_2_4")
temp_options = pkg_resources.resource_filename(
"letsencrypt.client", os.path.basename(CONFIG.OPTIONS_SSL_CONF))
shutil.copyfile(temp_options, os.path.join(CONFIG_DIR, "options-ssl.conf"))
# pylint: disable=invalid-name
def tearDownModule():
"""Run once after all unittests."""
shutil.rmtree(TEMP_DIR)
shutil.rmtree(CONFIG_DIR)
shutil.rmtree(WORK_DIR)
class TwoVhost80(unittest.TestCase):
"""Standard two http vhosts that are well configured."""
def setUp(self): # pylint: disable=invalid-name
"""Run before each and every test."""
with mock.patch("letsencrypt.client.apache_configurator."
"subprocess.Popen") as mock_popen:
# This just states that the ssl module is already loaded
mock_popen.return_value = MyPopen(("ssl_module", ""))
# Final slash is currently important
self.config_path = os.path.join(TEMP_DIR, "two_vhost_80/apache2/")
self.ssl_options = os.path.join(CONFIG_DIR, "options-ssl.conf")
backups = os.path.join(WORK_DIR, "backups")
self.config = apache_configurator.ApacheConfigurator(
self.config_path,
{"backup": backups,
"temp": os.path.join(WORK_DIR, "temp_checkpoint"),
"progress": os.path.join(backups, "IN_PROGRESS"),
"config": CONFIG_DIR,
"work": WORK_DIR},
self.ssl_options,
(2, 4, 7))
self.aug_path = "/files" + self.config_path
prefix = os.path.join(TEMP_DIR, "two_vhost_80/apache2/sites-available/")
aug_pre = "/files" + prefix
self.vh_truth = []
self.vh_truth.append(apache_configurator.VH(
os.path.join(prefix + "encryption-example.conf"),
os.path.join(aug_pre, "encryption-example.conf/VirtualHost"),
["*:80"], False, True))
self.vh_truth.append(apache_configurator.VH(
os.path.join(prefix, "default-ssl.conf"),
os.path.join(aug_pre, "default-ssl.conf/IfModule/VirtualHost"),
["_default_:443"], True, False))
self.vh_truth.append(apache_configurator.VH(
os.path.join(prefix, "000-default.conf"),
os.path.join(aug_pre, "000-default.conf/VirtualHost"),
["*:80"], False, True))
self.vh_truth.append(apache_configurator.VH(
os.path.join(prefix, "letsencrypt.conf"),
os.path.join(aug_pre, "letsencrypt.conf/VirtualHost"),
["*:80"], False, True))
self.vh_truth[0].add_name("encryption-example.demo")
self.vh_truth[2].add_name("ip-172-30-0-17")
self.vh_truth[3].add_name("letsencrypt.demo")
# pylint: disable=protected-access
def test_parse_file(self):
"""test parse_file.
letsencrypt.conf is chosen as the test file as it will not be
included during the normal course of execution.
"""
file_path = os.path.join(
self.config_path, "sites-available", "letsencrypt.conf")
self.config._parse_file(file_path)
# search for the httpd incl
matches = self.config.aug.match(
"/augeas/load/Httpd/incl [. ='%s']" % file_path)
self.assertTrue(matches)
def test_get_all_names(self):
"""test get_all_names."""
names = self.config.get_all_names()
self.assertEqual(set(names), set(
['letsencrypt.demo', 'encryption-example.demo', 'ip-172-30-0-17']))
def test_find_directive(self):
"""test find_directive."""
test = self.config.find_directive(
apache_configurator.case_i("Listen"), "443")
# This will only look in enabled hosts
test2 = self.config.find_directive(
apache_configurator.case_i("documentroot"))
self.assertEqual(len(test), 2)
self.assertEqual(len(test2), 3)
def test_get_virtual_hosts(self):
"""inefficient get_virtual_hosts check."""
vhs = self.config.get_virtual_hosts()
self.assertTrue(len(vhs) == 4)
found = 0
for vhost in vhs:
for truth in self.vh_truth:
if vhost == truth:
found += 1
break
self.assertEqual(found, 4)
def test_is_site_enabled(self):
"""test is_site_enabled"""
self.assertTrue(self.config.is_site_enabled(self.vh_truth[0].filep))
self.assertTrue(not self.config.is_site_enabled(self.vh_truth[1].filep))
self.assertTrue(self.config.is_site_enabled(self.vh_truth[2].filep))
self.assertTrue(self.config.is_site_enabled(self.vh_truth[3].filep))
def test_add_dir(self):
"""test add_dir."""
aug_default = "/files" + self.config.location["default"]
self.config.add_dir(
aug_default, "AddDirective", "test")
self.assertTrue(
self.config.find_directive("AddDirective", "test", aug_default))
def test_deploy_cert(self):
"""This test modifies the default-ssl vhost SSL directives."""
self.config.deploy_cert(
self.vh_truth[1],
"example/cert.pem", "example/key.pem", "example/cert_chain.pem")
loc_cert = self.config.find_directive(
apache_configurator.case_i("sslcertificatefile"),
re.escape("example/cert.pem"), self.vh_truth[1].path)
loc_key = self.config.find_directive(
apache_configurator.case_i("sslcertificateKeyfile"),
re.escape("example/key.pem"), self.vh_truth[1].path)
loc_chain = self.config.find_directive(
apache_configurator.case_i("SSLCertificateChainFile"),
re.escape("example/cert_chain.pem"), self.vh_truth[1].path)
# Verify one directive was found in the correct file
self.assertEqual(len(loc_cert), 1)
self.assertEqual(apache_configurator.get_file_path(loc_cert[0]),
self.vh_truth[1].filep)
self.assertEqual(len(loc_key), 1)
self.assertEqual(apache_configurator.get_file_path(loc_key[0]),
self.vh_truth[1].filep)
self.assertTrue(len(loc_chain), 1)
self.assertTrue(apache_configurator.get_file_path(loc_chain[0]),
self.vh_truth[1].filep)
def test_is_name_vhost(self):
"""test is_name_vhost."""
self.assertTrue(self.config.is_name_vhost("*:80"))
self.config.version = (2, 2)
self.assertFalse(self.config.is_name_vhost("*:80"))
def test_add_name_vhost(self):
"""test add_name_vhost."""
self.config.add_name_vhost("*:443")
# self.config.save(temporary=True)
self.assertTrue(self.config.find_directive(
"NameVirtualHost", re.escape("*:443")))
# pylint: disable=protected-access
def test_add_dir_to_ifmodssl(self):
"""test _add_dir_to_ifmodssl.
Path must be valid before attempting to add to augeas
"""
self.config._add_dir_to_ifmodssl(
"/files" + self.config.location["default"], "FakeDirective", "123")
matches = self.config.find_directive("FakeDirective", "123")
self.assertEqual(len(matches), 1)
self.assertTrue("IfModule" in matches[0])
def test_make_vhost_ssl(self):
"""test make_vhost_ssl."""
ssl_vhost = self.config.make_vhost_ssl(self.vh_truth[0])
self.assertTrue(
ssl_vhost.filep ==
os.path.join(self.config_path, "sites-available",
"encryption-example-le-ssl.conf"))
self.assertTrue(ssl_vhost.path ==
"/files" + ssl_vhost.filep + "/IfModule/VirtualHost")
self.assertTrue(ssl_vhost.addrs == ["*:443"])
self.assertTrue(ssl_vhost.names == ["encryption-example.demo"])
self.assertTrue(ssl_vhost.ssl)
self.assertFalse(ssl_vhost.enabled)
self.assertTrue(self.config.find_directive(
"SSLCertificateFile", None, ssl_vhost.path))
self.assertTrue(self.config.find_directive(
"SSLCertificateKeyFile", None, ssl_vhost.path))
self.assertTrue(self.config.find_directive(
"Include", self.ssl_options, ssl_vhost.path))
self.assertEqual(self.config.is_name_vhost(self.vh_truth[0]),
self.config.is_name_vhost(ssl_vhost))
self.assertEqual(len(self.config.vhosts), 5)
@mock.patch("letsencrypt.client.apache_configurator."
"subprocess.Popen")
def test_get_version(self, mock_popen):
"""test get_version."""
mock_popen.return_value = MyPopen(
("Server Version: Apache/2.4.2 (Debian)", ""))
self.assertEqual(self.config.get_version(), (2, 4, 2))
mock_popen.return_value = MyPopen(
("Server Version: Apache/2 (Linux)", ""))
self.assertEqual(self.config.get_version(), tuple([2]))
mock_popen.return_value = MyPopen(
("Server Version: Apache (Debian)", ""))
self.assertRaises(
errors.LetsEncryptConfiguratorError, self.config.get_version)
mock_popen.return_value = MyPopen(
("Server Version: Apache/2.3\n Apache/2.4.7", ""))
self.assertRaises(
errors.LetsEncryptConfiguratorError, self.config.get_version)
mock_popen.side_effect = OSError("Can't find program")
self.assertRaises(
errors.LetsEncryptConfiguratorError, self.config.get_version)
# def _verify_redirect(self, config_path):
# """Verifies that the vhost contains the REWRITE."""
# with open(config_path, 'r') as config_fd:
# conf = config_fd.read()
# return CONFIG.REWRITE_HTTPS_ARGS[1] in conf
# def debug_file(filepath):
# """Print out the file."""
# with open(filepath, 'r')as file_d:
# print file_d.read()
# I am sure there is a cleaner way to do this... but it works
# pylint: disable=too-few-public-methods
class MyPopen(object):
"""Made for mock popen object."""
def __init__(self, tup):
self.tup = tup
def communicate(self): # pylint: disable=no-self-use
"""Simply return that ssl_module is in output."""
return self.tup
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,198 @@
# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.
# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
#
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
#
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
# Global configuration
#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
Mutex file:${APACHE_LOCK_DIR} default
#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 300
#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On
#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100
#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5
# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log
#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn
# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
# Include list of ports to listen on
Include ports.conf
# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives. See also the AllowOverride
# directive.
#
AccessFileName .htaccess
#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.
# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf
# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,4 @@
# Define an access log for VirtualHosts that don't define their own logfile
CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,31 @@
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of: Full | OS | Minimal | Minor | Major | Prod
# where Full conveys the most information, and Prod the least.
#ServerTokens Minimal
ServerTokens OS
#ServerTokens Full
#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of: On | Off | EMail
#ServerSignature Off
ServerSignature On
#
# Allow TRACE method
#
# Set to "extended" to also reflect the request body (only for testing and
# diagnostic purposes).
#
# Set to one of: On | Off | extended
TraceEnable Off
#TraceEnable On
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,20 @@
<IfModule mod_alias.c>
<IfModule mod_cgi.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>
<IfModule mod_cgid.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>
<IfDefine ENABLE_USR_LIB_CGI_BIN>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</IfDefine>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1 @@
../conf-available/other-vhosts-access-log.conf

View File

@@ -0,0 +1 @@
../conf-available/security.conf

View File

@@ -0,0 +1 @@
../conf-available/serve-cgi-bin.conf

View File

@@ -0,0 +1,28 @@
# envvars - default environment variables for apache2ctl
# this won't be correct after changing uid
unset HOME
# for supporting multiple apache2 instances
if [ "${APACHE_CONFDIR##/etc/apache2-}" != "${APACHE_CONFDIR}" ] ; then
SUFFIX="-${APACHE_CONFDIR##/etc/apache2-}"
else
SUFFIX=
fi
# Since there is no sane way to get the parsed apache2 config in scripts, some
# settings are defined via environment variables and then used in apache2ctl,
# /etc/init.d/apache2, /etc/logrotate.d/apache2, etc.
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
# temporary state file location. This might be changed to /run in Wheezy+1
export APACHE_PID_FILE=/var/run/apache2/apache2$SUFFIX.pid
export APACHE_RUN_DIR=/var/run/apache2$SUFFIX
export APACHE_LOCK_DIR=/var/lock/apache2$SUFFIX
# Only /var/log/apache2 is handled by /etc/logrotate.d/apache2.
export APACHE_LOG_DIR=/var/log/apache2$SUFFIX
## The locale used by some modules like mod_dav
export LANG=C
export LANG

View File

@@ -0,0 +1,89 @@
<IfModule mod_ssl.c>
# Pseudo Random Number Generator (PRNG):
# Configure one or more sources to seed the PRNG of the SSL library.
# The seed data should be of good random quality.
# WARNING! On some platforms /dev/random blocks if not enough entropy
# is available. This means you then cannot use the /dev/random device
# because it would lead to very long connection times (as long as
# it requires to make more entropy available). But usually those
# platforms additionally provide a /dev/urandom device which doesn't
# block. So, if available, use this one instead. Read the mod_ssl User
# Manual for more details.
#
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 512
##
## SSL Global Context
##
## All SSL configuration in this context applies both to
## the main server and all SSL-enabled virtual hosts.
##
#
# Some MIME-types for downloading Certificates and CRLs
#
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
# Pass Phrase Dialog:
# Configure the pass phrase gathering process.
# The filtering dialog program (`builtin' is a internal
# terminal dialog) has to provide the pass phrase on stdout.
SSLPassPhraseDialog exec:/usr/share/apache2/ask-for-passphrase
# Inter-Process Session Cache:
# Configure the SSL Session Cache: First the mechanism
# to use and second the expiring timeout (in seconds).
# (The mechanism dbm has known memory leaks and should not be used).
#SSLSessionCache dbm:${APACHE_RUN_DIR}/ssl_scache
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout 300
# Semaphore:
# Configure the path to the mutual exclusion semaphore the
# SSL engine uses internally for inter-process synchronization.
# (Disabled by default, the global Mutex directive consolidates by default
# this)
#Mutex file:${APACHE_LOCK_DIR}/ssl_mutex ssl-cache
# SSL Cipher Suite:
# List the ciphers that the client is permitted to negotiate. See the
# ciphers(1) man page from the openssl package for list of all available
# options.
# Enable only secure ciphers:
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
# Speed-optimized SSL Cipher configuration:
# If speed is your main concern (on busy HTTPS servers e.g.),
# you might want to force clients to specific, performance
# optimized ciphers. In this case, prepend those ciphers
# to the SSLCipherSuite list, and enable SSLHonorCipherOrder.
# Caveat: by giving precedence to RC4-SHA and AES128-SHA
# (as in the example below), most connections will no longer
# have perfect forward secrecy - if the server's key is
# compromised, captures of past or future traffic must be
# considered compromised, too.
#SSLCipherSuite RC4-SHA:AES128-SHA:HIGH:MEDIUM:!aNULL:!MD5
#SSLHonorCipherOrder on
# The protocols to enable.
# Available values: all, SSLv3, TLSv1, TLSv1.1, TLSv1.2
# SSL v2 is no longer supported
SSLProtocol all
# Allow insecure renegotiation with clients which do not yet support the
# secure renegotiation protocol. Default: Off
#SSLInsecureRenegotiation on
# Whether to forbid non-SNI clients to access name based virtual hosts.
# Default: Off
#SSLStrictSNIVHostCheck On
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,2 @@
# Depends: setenvif mime socache_shmcb
LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so

View File

@@ -0,0 +1,20 @@
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf
Listen 80
NameVirtualHost *:80
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
<IfModule mod_ssl.c>
NameVirtualHost *:443
</IfModule>

View File

@@ -0,0 +1,11 @@
<VirtualHost *:80>
# How well does Let's Encrypt work without a ServerName/Alias?
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,38 @@
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# A self-signed (snakeoil) certificate can be created by installing
# the ssl-cert package. See
# /usr/share/doc/apache2/README.Debian.gz for more info.
# If both key and certificate are stored in the same file, only the
# SSLCertificateFile directive is needed.
SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem
SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
# MSIE 7 and newer should be able to use keepalive
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1 @@
../sites-available/000-default.conf

View File

@@ -0,0 +1 @@
sites-available/000-default.conf, default.com

View File

@@ -0,0 +1,196 @@
# This is the main Apache server configuration file. It contains the
# configuration directives that give the server its instructions.
# See http://httpd.apache.org/docs/2.4/ for detailed information about
# the directives and /usr/share/doc/apache2/README.Debian about Debian specific
# hints.
#
#
# Summary of how the Apache 2 configuration works in Debian:
# The Apache 2 web server configuration in Debian is quite different to
# upstream's suggested way to configure the web server. This is because Debian's
# default Apache2 installation attempts to make adding and removing modules,
# virtual hosts, and extra configuration directives as flexible as possible, in
# order to make automating the changes and administering the server as easy as
# possible.
# It is split into several files forming the configuration hierarchy outlined
# below, all located in the /etc/apache2/ directory:
#
# /etc/apache2/
# |-- apache2.conf
# | `-- ports.conf
# |-- mods-enabled
# | |-- *.load
# | `-- *.conf
# |-- conf-enabled
# | `-- *.conf
# `-- sites-enabled
# `-- *.conf
#
#
# * apache2.conf is the main configuration file (this file). It puts the pieces
# together by including all remaining configuration files when starting up the
# web server.
#
# * ports.conf is always included from the main configuration file. It is
# supposed to determine listening ports for incoming connections which can be
# customized anytime.
#
# * Configuration files in the mods-enabled/, conf-enabled/ and sites-enabled/
# directories contain particular configuration snippets which manage modules,
# global configuration fragments, or virtual host configurations,
# respectively.
#
# They are activated by symlinking available configuration files from their
# respective *-available/ counterparts. These should be managed by using our
# helpers a2enmod/a2dismod, a2ensite/a2dissite and a2enconf/a2disconf. See
# their respective man pages for detailed information.
#
# * The binary is called apache2. Due to the use of environment variables, in
# the default configuration, apache2 needs to be started/stopped with
# /etc/init.d/apache2 or apache2ctl. Calling /usr/bin/apache2 directly will not
# work with the default configuration.
# Global configuration
#
# The accept serialization lock file MUST BE STORED ON A LOCAL DISK.
#
Mutex file:${APACHE_LOCK_DIR} default
#
# PidFile: The file in which the server should record its process
# identification number when it starts.
# This needs to be set in /etc/apache2/envvars
#
PidFile ${APACHE_PID_FILE}
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 300
#
# KeepAlive: Whether or not to allow persistent connections (more than
# one request per connection). Set to "Off" to deactivate.
#
KeepAlive On
#
# MaxKeepAliveRequests: The maximum number of requests to allow
# during a persistent connection. Set to 0 to allow an unlimited amount.
# We recommend you leave this number high, for maximum performance.
#
MaxKeepAliveRequests 100
#
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5
# These need to be set in /etc/apache2/envvars
User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}
#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog ${APACHE_LOG_DIR}/error.log
#
# LogLevel: Control the severity of messages logged to the error_log.
# Available values: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the log level for particular modules, e.g.
# "LogLevel info ssl:warn"
#
LogLevel warn
# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
# Include list of ports to listen on
Include ports.conf
# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory /usr/share>
AllowOverride None
Require all granted
</Directory>
<Directory /var/>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives. See also the AllowOverride
# directive.
#
AccessFileName .htaccess
#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
# Include of directories ignores editors' and dpkg's backup files,
# see README.Debian for details.
# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf
# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,4 @@
# Define an access log for VirtualHosts that don't define their own logfile
CustomLog ${APACHE_LOG_DIR}/other_vhosts_access.log vhost_combined
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,35 @@
# Changing the following options will not really affect the security of the
# server, but might make attacks slightly more difficult in some cases.
#
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of: Full | OS | Minimal | Minor | Major | Prod
# where Full conveys the most information, and Prod the least.
#ServerTokens Minimal
ServerTokens OS
#ServerTokens Full
#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of: On | Off | EMail
#ServerSignature Off
ServerSignature On
#
# Allow TRACE method
#
# Set to "extended" to also reflect the request body (only for testing and
# diagnostic purposes).
#
# Set to one of: On | Off | extended
TraceEnable Off
#TraceEnable On
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,20 @@
<IfModule mod_alias.c>
<IfModule mod_cgi.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>
<IfModule mod_cgid.c>
Define ENABLE_USR_LIB_CGI_BIN
</IfModule>
<IfDefine ENABLE_USR_LIB_CGI_BIN>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</IfDefine>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1 @@
../conf-available/other-vhosts-access-log.conf

View File

@@ -0,0 +1 @@
../conf-available/security.conf

View File

@@ -0,0 +1 @@
../conf-available/serve-cgi-bin.conf

View File

@@ -0,0 +1,29 @@
# envvars - default environment variables for apache2ctl
# this won't be correct after changing uid
unset HOME
# for supporting multiple apache2 instances
if [ "${APACHE_CONFDIR##/etc/apache2-}" != "${APACHE_CONFDIR}" ] ; then
SUFFIX="-${APACHE_CONFDIR##/etc/apache2-}"
else
SUFFIX=
fi
# Since there is no sane way to get the parsed apache2 config in scripts, some
# settings are defined via environment variables and then used in apache2ctl,
# /etc/init.d/apache2, /etc/logrotate.d/apache2, etc.
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
# temporary state file location. This might be changed to /run in Wheezy+1
export APACHE_PID_FILE=/var/run/apache2/apache2$SUFFIX.pid
export APACHE_RUN_DIR=/var/run/apache2$SUFFIX
export APACHE_LOCK_DIR=/var/lock/apache2$SUFFIX
# Only /var/log/apache2 is handled by /etc/logrotate.d/apache2.
export APACHE_LOG_DIR=/var/log/apache2$SUFFIX
## The locale used by some modules like mod_dav
export LANG=C
export LANG

View File

@@ -0,0 +1,89 @@
<IfModule mod_ssl.c>
# Pseudo Random Number Generator (PRNG):
# Configure one or more sources to seed the PRNG of the SSL library.
# The seed data should be of good random quality.
# WARNING! On some platforms /dev/random blocks if not enough entropy
# is available. This means you then cannot use the /dev/random device
# because it would lead to very long connection times (as long as
# it requires to make more entropy available). But usually those
# platforms additionally provide a /dev/urandom device which doesn't
# block. So, if available, use this one instead. Read the mod_ssl User
# Manual for more details.
#
SSLRandomSeed startup builtin
SSLRandomSeed startup file:/dev/urandom 512
SSLRandomSeed connect builtin
SSLRandomSeed connect file:/dev/urandom 512
##
## SSL Global Context
##
## All SSL configuration in this context applies both to
## the main server and all SSL-enabled virtual hosts.
##
#
# Some MIME-types for downloading Certificates and CRLs
#
AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl
# Pass Phrase Dialog:
# Configure the pass phrase gathering process.
# The filtering dialog program (`builtin' is a internal
# terminal dialog) has to provide the pass phrase on stdout.
SSLPassPhraseDialog exec:/usr/share/apache2/ask-for-passphrase
# Inter-Process Session Cache:
# Configure the SSL Session Cache: First the mechanism
# to use and second the expiring timeout (in seconds).
# (The mechanism dbm has known memory leaks and should not be used).
#SSLSessionCache dbm:${APACHE_RUN_DIR}/ssl_scache
SSLSessionCache shmcb:${APACHE_RUN_DIR}/ssl_scache(512000)
SSLSessionCacheTimeout 300
# Semaphore:
# Configure the path to the mutual exclusion semaphore the
# SSL engine uses internally for inter-process synchronization.
# (Disabled by default, the global Mutex directive consolidates by default
# this)
#Mutex file:${APACHE_LOCK_DIR}/ssl_mutex ssl-cache
# SSL Cipher Suite:
# List the ciphers that the client is permitted to negotiate. See the
# ciphers(1) man page from the openssl package for list of all available
# options.
# Enable only secure ciphers:
SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
# Speed-optimized SSL Cipher configuration:
# If speed is your main concern (on busy HTTPS servers e.g.),
# you might want to force clients to specific, performance
# optimized ciphers. In this case, prepend those ciphers
# to the SSLCipherSuite list, and enable SSLHonorCipherOrder.
# Caveat: by giving precedence to RC4-SHA and AES128-SHA
# (as in the example below), most connections will no longer
# have perfect forward secrecy - if the server's key is
# compromised, captures of past or future traffic must be
# considered compromised, too.
#SSLCipherSuite RC4-SHA:AES128-SHA:HIGH:MEDIUM:!aNULL:!MD5
#SSLHonorCipherOrder on
# The protocols to enable.
# Available values: all, SSLv3, TLSv1, TLSv1.1, TLSv1.2
# SSL v2 is no longer supported
SSLProtocol all
# Allow insecure renegotiation with clients which do not yet support the
# secure renegotiation protocol. Default: Off
#SSLInsecureRenegotiation on
# Whether to forbid non-SNI clients to access name based virtual hosts.
# Default: Off
#SSLStrictSNIVHostCheck On
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,2 @@
# Depends: setenvif mime socache_shmcb
LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so

View File

@@ -0,0 +1,15 @@
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf
Listen 80
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,12 @@
<VirtualHost *:80>
ServerName ip-172-30-0-17
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,40 @@
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# A self-signed (snakeoil) certificate can be created by installing
# the ssl-cert package. See
# /usr/share/doc/apache2/README.Debian.gz for more info.
# If both key and certificate are stored in the same file, only the
# SSLCertificateFile directive is needed.
SSLCertificateFile /etc/apache2/certs/letsencrypt-cert_5.pem
SSLCertificateKeyFile /etc/apache2/ssl/key-letsencrypt_15.pem
#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
# MSIE 7 and newer should be able to use keepalive
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
</VirtualHost>
</IfModule>
# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

View File

@@ -0,0 +1,42 @@
<VirtualHost *:80>
ServerName encryption-example.demo
ServerAdmin webmaster@localhost
DocumentRoot /var/www-encryption-example/static/
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
Alias /doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
</VirtualHost>

View File

@@ -0,0 +1,42 @@
<VirtualHost *:80>
ServerName letsencrypt.demo
ServerAdmin webmaster@localhost
DocumentRoot /var/www-letsencrypt-reworld/static/
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory "/usr/lib/cgi-bin">
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
Alias /doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
Options Indexes MultiViews FollowSymLinks
AllowOverride None
Order deny,allow
Deny from all
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
</VirtualHost>

View File

@@ -0,0 +1 @@
../sites-available/000-default.conf

View File

@@ -0,0 +1 @@
../sites-available/encryption-example.conf

View File

@@ -0,0 +1 @@
../sites-available/letsencrypt.conf

View File

@@ -0,0 +1,2 @@
sites-available/letsencrypt.conf, letencrypt.demo
sites-available/encryption-example.conf, encryption-example.demo

View File

@@ -4,3 +4,4 @@ jsonschema==2.4.0
python-augeas==0.5.0
requests==2.4.3
argparse==1.2.2
mock==1.0.1

View File

@@ -19,6 +19,7 @@ docs_extras = [
testing_extras = [
'coverage',
'mock',
'nose',
'nosexcover',
'pylint<1.4', # py2.6 compat, c.f #97