diff --git a/MANIFEST.in b/MANIFEST.in index ee9e4f641..3320716f8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ recursive-include letsencrypt *.json recursive-include letsencrypt *.sh recursive-include letsencrypt *.conf +recursive include letsencrypt/client/tests/testdata * diff --git a/letsencrypt/client/CONFIG.py b/letsencrypt/client/CONFIG.py index 1c2fd496c..3cc9d09a6 100644 --- a/letsencrypt/client/CONFIG.py +++ b/letsencrypt/client/CONFIG.py @@ -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.""" diff --git a/letsencrypt/client/acme.py b/letsencrypt/client/acme.py index 138d0d851..13a0dcc91 100644 --- a/letsencrypt/client/acme.py +++ b/letsencrypt/client/acme.py @@ -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 [ diff --git a/letsencrypt/client/apache_configurator.py b/letsencrypt/client/apache_configurator.py index ec35199c1..0a2f155f5 100644 --- a/letsencrypt/client/apache_configurator.py +++ b/letsencrypt/client/apache_configurator.py @@ -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 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 = " \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 += " \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 (" \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" + " \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 (" \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" - " \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) diff --git a/letsencrypt/client/augeas_configurator.py b/letsencrypt/client/augeas_configurator.py index 5a81ed5f7..9aa4ac75b 100644 --- a/letsencrypt/client/augeas_configurator.py +++ b/letsencrypt/client/augeas_configurator.py @@ -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 diff --git a/letsencrypt/client/client.py b/letsencrypt/client/client.py index 006f6289e..8dddae41a 100644 --- a/letsencrypt/client/client.py +++ b/letsencrypt/client/client.py @@ -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: diff --git a/letsencrypt/client/recovery_token_challenge.py b/letsencrypt/client/recovery_token_challenge.py index c47639020..04a3d3ec9 100644 --- a/letsencrypt/client/recovery_token_challenge.py +++ b/letsencrypt/client/recovery_token_challenge.py @@ -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 diff --git a/letsencrypt/client/tests/__init__.py b/letsencrypt/client/tests/__init__.py new file mode 100644 index 000000000..d9db68022 --- /dev/null +++ b/letsencrypt/client/tests/__init__.py @@ -0,0 +1 @@ +"""Let's Encrypt Tests""" diff --git a/letsencrypt/client/acme_test.py b/letsencrypt/client/tests/acme_test.py similarity index 100% rename from letsencrypt/client/acme_test.py rename to letsencrypt/client/tests/acme_test.py diff --git a/letsencrypt/client/tests/apache_configurator_test.py b/letsencrypt/client/tests/apache_configurator_test.py new file mode 100644 index 000000000..daad871a0 --- /dev/null +++ b/letsencrypt/client/tests/apache_configurator_test.py @@ -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() diff --git a/letsencrypt/client/le_util_test.py b/letsencrypt/client/tests/le_util_test.py similarity index 100% rename from letsencrypt/client/le_util_test.py rename to letsencrypt/client/tests/le_util_test.py diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf new file mode 100644 index 000000000..4ed016e07 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/apache2.conf @@ -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 +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# 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. + + Options FollowSymLinks + AllowOverride None + Require all denied + + + + AllowOverride None + Require all granted + + + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + + +# 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. +# + + Require all denied + + + +# +# 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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf new file mode 100644 index 000000000..5e9f5e9e7 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/other-vhosts-access-log.conf @@ -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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf new file mode 100644 index 000000000..1dfe33c60 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/security.conf @@ -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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf new file mode 100644 index 000000000..b02782dab --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-available/serve-cgi-bin.conf @@ -0,0 +1,20 @@ + + + Define ENABLE_USR_LIB_CGI_BIN + + + + Define ENABLE_USR_LIB_CGI_BIN + + + + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + AllowOverride None + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + Require all granted + + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf new file mode 120000 index 000000000..8af91e530 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/other-vhosts-access-log.conf @@ -0,0 +1 @@ +../conf-available/other-vhosts-access-log.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf new file mode 120000 index 000000000..036c97fa7 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/security.conf @@ -0,0 +1 @@ +../conf-available/security.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf new file mode 120000 index 000000000..d917f688e --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/conf-enabled/serve-cgi-bin.conf @@ -0,0 +1 @@ +../conf-available/serve-cgi-bin.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars new file mode 100644 index 000000000..8051c4544 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/envvars @@ -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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf new file mode 100644 index 000000000..e9fcf4f9b --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.conf @@ -0,0 +1,89 @@ + + + # 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 + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load new file mode 100644 index 000000000..3d2336ae0 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/mods-available/ssl.load @@ -0,0 +1,2 @@ +# Depends: setenvif mime socache_shmcb +LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf new file mode 100644 index 000000000..176b9d103 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/ports.conf @@ -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 + + + Listen 443 + + + + Listen 443 + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet + +NameVirtualHost *:443 + diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf new file mode 100644 index 000000000..8da335d35 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/000-default.conf @@ -0,0 +1,11 @@ + + # 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 + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf new file mode 100644 index 000000000..2fbfc02a8 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-available/default-ssl.conf @@ -0,0 +1,38 @@ + + + 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 +StdEnvVars + + + SSLOptions +StdEnvVars + + + 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 + + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf new file mode 120000 index 000000000..3c4632b73 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/apache2/sites-enabled/000-default.conf @@ -0,0 +1 @@ +../sites-available/000-default.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/sites b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/sites new file mode 100644 index 000000000..03d53dd61 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/default_vhost/sites @@ -0,0 +1 @@ +sites-available/000-default.conf, default.com diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf new file mode 100644 index 000000000..2a5bb7be2 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/apache2.conf @@ -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 +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# 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. + + Options FollowSymLinks + AllowOverride None + Require all denied + + + + AllowOverride None + Require all granted + + + + Options Indexes FollowSymLinks + AllowOverride None + Require all granted + + +# 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. +# + + Require all denied + + +# 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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf new file mode 100644 index 000000000..5e9f5e9e7 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/other-vhosts-access-log.conf @@ -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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf new file mode 100644 index 000000000..eccfcb1fd --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/security.conf @@ -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 diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf new file mode 100644 index 000000000..b02782dab --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-available/serve-cgi-bin.conf @@ -0,0 +1,20 @@ + + + Define ENABLE_USR_LIB_CGI_BIN + + + + Define ENABLE_USR_LIB_CGI_BIN + + + + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + AllowOverride None + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + Require all granted + + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf new file mode 120000 index 000000000..8af91e530 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/other-vhosts-access-log.conf @@ -0,0 +1 @@ +../conf-available/other-vhosts-access-log.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf new file mode 120000 index 000000000..036c97fa7 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/security.conf @@ -0,0 +1 @@ +../conf-available/security.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf new file mode 120000 index 000000000..d917f688e --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/conf-enabled/serve-cgi-bin.conf @@ -0,0 +1 @@ +../conf-available/serve-cgi-bin.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars new file mode 100644 index 000000000..a13d9a89e --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/envvars @@ -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 + diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf new file mode 100644 index 000000000..e9fcf4f9b --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.conf @@ -0,0 +1,89 @@ + + + # 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 + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load new file mode 100644 index 000000000..3d2336ae0 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/mods-available/ssl.load @@ -0,0 +1,2 @@ +# Depends: setenvif mime socache_shmcb +LoadModule ssl_module /usr/lib/apache2/modules/mod_ssl.so diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf new file mode 100644 index 000000000..5daec58c1 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/ports.conf @@ -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 + + + Listen 443 + + + + Listen 443 + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf new file mode 100644 index 000000000..c759768c5 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/000-default.conf @@ -0,0 +1,12 @@ + + + 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 + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf new file mode 100644 index 000000000..f1061c928 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/default-ssl.conf @@ -0,0 +1,40 @@ + + + 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 + + SSLOptions +StdEnvVars + + + SSLOptions +StdEnvVars + + + 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 + + + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf new file mode 100644 index 000000000..4786bda13 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/encryption-example.conf @@ -0,0 +1,42 @@ + + ServerName encryption-example.demo + ServerAdmin webmaster@localhost + + DocumentRoot /var/www-encryption-example/static/ + + Options FollowSymLinks + AllowOverride None + + + Options Indexes FollowSymLinks MultiViews + AllowOverride None + Order allow,deny + allow from all + + + ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + + AllowOverride None + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + Order allow,deny + Allow from all + + + 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/" + + Options Indexes MultiViews FollowSymLinks + AllowOverride None + Order deny,allow + Deny from all + Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf new file mode 100644 index 000000000..e38fc9f9b --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-available/letsencrypt.conf @@ -0,0 +1,42 @@ + +ServerName letsencrypt.demo +ServerAdmin webmaster@localhost + +DocumentRoot /var/www-letsencrypt-reworld/static/ + +Options FollowSymLinks +AllowOverride None + + +Options Indexes FollowSymLinks MultiViews +AllowOverride None +Order allow,deny +allow from all + + +ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/ + +AllowOverride None +Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch +Order allow,deny +Allow from all + + +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/" + +Options Indexes MultiViews FollowSymLinks +AllowOverride None +Order deny,allow +Deny from all +Allow from 127.0.0.0/255.0.0.0 ::1/128 + + + diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf new file mode 120000 index 000000000..3c4632b73 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/000-default.conf @@ -0,0 +1 @@ +../sites-available/000-default.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf new file mode 120000 index 000000000..417818069 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/encryption-example.conf @@ -0,0 +1 @@ +../sites-available/encryption-example.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf new file mode 120000 index 000000000..f31102913 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/apache2/sites-enabled/letsencrypt.conf @@ -0,0 +1 @@ +../sites-available/letsencrypt.conf \ No newline at end of file diff --git a/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/sites b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/sites new file mode 100644 index 000000000..5a7ad7622 --- /dev/null +++ b/letsencrypt/client/tests/testdata/debian_apache_2_4/two_vhost_80/sites @@ -0,0 +1,2 @@ +sites-available/letsencrypt.conf, letencrypt.demo +sites-available/encryption-example.conf, encryption-example.demo diff --git a/requirements.txt b/requirements.txt index c839c21c4..a95a0807f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/setup.py b/setup.py index 24ff3752d..462759c1c 100755 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ docs_extras = [ testing_extras = [ 'coverage', + 'mock', 'nose', 'nosexcover', 'pylint<1.4', # py2.6 compat, c.f #97