diff --git a/letsencrypt/client/acme.py b/letsencrypt/client/acme.py index 392546b29..1a04cdc9d 100644 --- a/letsencrypt/client/acme.py +++ b/letsencrypt/client/acme.py @@ -10,19 +10,18 @@ from letsencrypt.client import le_util SCHEMATA = dict([ (schema, json.load(open(pkg_resources.resource_filename( - __name__, "schemata/%s.json" % schema)))) for schema in [ - "authorization", - "authorizationRequest", - "certificate", - "certificateRequest", - "challenge", - "challengeRequest", - "defer", - "error", - "revocation", - "revocationRequest", - "statusRequest", - ] + __name__, "schemata/%s.json" % schema)))) + for schema in ["authorization", + "authorizationRequest", + "certificate", + "certificateRequest", + "challenge", + "challengeRequest", + "defer", + "error", + "revocation", + "revocationRequest", + "statusRequest"] ]) diff --git a/letsencrypt/client/acme_test.py b/letsencrypt/client/acme_test.py index b4d552c0b..9c4dc6e1e 100644 --- a/letsencrypt/client/acme_test.py +++ b/letsencrypt/client/acme_test.py @@ -10,10 +10,10 @@ class ACMEObjectValidateTest(unittest.TestCase): def setUp(self): self.schemata = { 'foo': { - 'type' : 'object', - 'properties' : { - 'price' : {'type' : 'number'}, - 'name' : {'type' : 'string'}, + 'type': 'object', + 'properties': { + 'price': {'type': 'number'}, + 'name': {'type': 'string'}, }, }, } diff --git a/letsencrypt/client/apache_configurator.py b/letsencrypt/client/apache_configurator.py index b95d5c8bb..54b16af13 100644 --- a/letsencrypt/client/apache_configurator.py +++ b/letsencrypt/client/apache_configurator.py @@ -57,6 +57,7 @@ class VH(object): def add_name(self, name): self.names.append(name) + class ApacheConfigurator(augeas_configurator.AugeasConfigurator): """ State of Configurator: @@ -137,16 +138,20 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): search = {} path = {} - path["cert_file"] = self.find_directive(self.case_i("SSLCertificateFile"), None, vhost.path) - path["cert_key"] = self.find_directive(self.case_i("SSLCertificateKeyFile"), None, vhost.path) + path["cert_file"] = self.find_directive(case_i( + "SSLCertificateFile"), None, vhost.path) + path["cert_key"] = self.find_directive(case_i( + "SSLCertificateKeyFile"), None, vhost.path) # Only include if a certificate chain is specified if cert_chain is not None: - path["cert_chain"] = self.find_directive(self.case_i("SSLCertificateChainFile"), None, vhost.path) + path["cert_chain"] = self.find_directive( + case_i("SSLCertificateChainFile"), None, vhost.path) if len(path["cert_file"]) == 0 or len(path["cert_key"]) == 0: # Throw some "can't find all of the directives error" - logger.warn("Warn: cannot find a cert or key directive in " + vhost.path) + logger.warn(("Cannot find a cert or key directive in %s" + % vhost.path)) logger.warn("VirtualHost was not modified") # Presumably break here so that the virtualhost is not modified return False @@ -161,7 +166,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): else: 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) + self.save_notes += ("Changed vhost at %s with addresses of %s\n" % + (vhost.file, vhost.addrs)) self.save_notes += "\tSSLCertificateFile %s\n" % cert self.save_notes += "\tSSLCertificateKeyFile %s\n" % key if cert_chain: @@ -183,7 +189,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): return vh # Check for servernames/aliases for ssl hosts for v in self.vhosts: - if v.ssl == True: + if v.ssl: for n in v.names: if n == name: return v @@ -197,12 +203,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # Check for non ssl vhosts with servernames/aliases == 'name' for v in self.vhosts: - if v.ssl == False: + if not v.ssl: for n in v.names: if n == name: # When do we need to self.make_vhost_ssl(v) return self.make_vhost_ssl(v) - #return v # No matches, search for the default for v in self.vhosts: @@ -228,7 +233,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): all_names = set() # Kept in same function to avoid multiple compilations of the regex - priv_ip_regex = "(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)" + priv_ip_regex = ("(^127\.0\.0\.1)|(^10\.)|(^172\.1[6-9]\.)|" + "(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)") privateIPs = re.compile(priv_ip_regex) for v in self.vhosts: @@ -246,32 +252,35 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): return all_names - def __set_user_config_file(self, filename = ''): + def __set_user_config_file(self, filename=''): if filename: self.user_config_file = filename else: - # Basic check to see if httpd.conf exists and is included via direct include + # Basic check to see if httpd.conf exists and + # in heirarchy via direct include # httpd.conf was very common as a user file in Apache 2.2 - if os.path.isfile(self.server_root + 'httpd.conf') and self.find_directive(self.case_i("Include"), self.case_i("httpd.conf")): + if (os.path.isfile(self.server_root + 'httpd.conf') and + self.find_directive(case_i("Include"), + case_i("httpd.conf"))): self.user_config_file = self.server_root + 'httpd.conf' else: self.user_config_file = self.server_root + 'apache2.conf' - #def __is_private_ip(ipaddr): - # re.compile() - - def __add_servernames(self, host): """ Helper function for get_virtual_hosts() """ - nameMatch = self.aug.match("%s//*[self::directive=~regexp('%s')] | %s//*[self::directive=~regexp('%s')]" % (host.path, self.case_i('ServerName'), host.path, self.case_i('ServerAlias'))) + nameMatch = self.aug.match(("%s//*[self::directive=~regexp('%s')] | " + "%s//*[self::directive=~regexp('%s')]" % + (host.path, + case_i('ServerName'), + host.path, + case_i('ServerAlias')))) for name in nameMatch: args = self.aug.match(name + "/*") for arg in args: host.add_name(self.aug.get(arg)) - def __create_vhost(self, path): """ Private function used by get_virtual_hosts to create vhost objects @@ -281,20 +290,26 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): for arg in args: addrs.append(self.aug.get(arg)) is_ssl = False - if len(self.find_directive(self.case_i("SSLEngine"), self.case_i("on"), path)) > 0: + + if len(self.find_directive( + case_i("SSLEngine"), case_i("on"), path)) > 0: is_ssl = True + filename = self.get_file_path(path) is_enabled = self.is_site_enabled(filename) vhost = VH(filename, path, addrs, is_ssl, is_enabled) self.__add_servernames(vhost) return vhost + # TODO: make "sites-available" a configurable directory def get_virtual_hosts(self): """ Returns list of virtual hosts found in the Apache configuration """ - #Search sites-available, httpd.conf for possible virtual hosts - paths = self.aug.match("/files%ssites-available//*[label()=~regexp('%s')]" % (self.server_root, self.case_i('VirtualHost'))) + # Search sites-available, httpd.conf for possible virtual hosts + paths = self.aug.match( + ("/files%ssites-available//*[label()=~regexp('%s')]" % + (self.server_root, case_i('VirtualHost')))) vhs = [] for p in paths: vhs.append(self.__create_vhost(p)) @@ -309,7 +324,7 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # 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(self.case_i("NameVirtualHost"), None) + paths = self.find_directive(case_i("NameVirtualHost"), None) name_vh = [] for p in paths: name_vh.append(self.aug.get(p)) @@ -333,10 +348,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): aug_file_path = "/files%sports.conf" % self.server_root self.add_dir_to_ifmodssl(aug_file_path, "NameVirtualHost", addr) - if len(self.find_directive(self.case_i("NameVirtualHost"), self.case_i(addr))) == 0: + # 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.add_dir_to_ifmodssl("/files" + self.server_root + "httpd.conf", "NameVirtualHost", addr) + + self.add_dir_to_ifmodssl("/files%shttpd.conf" % self.server_root, + "NameVirtualHost", + addr) self.save_notes += 'Setting %s to be NameBasedVirtualHost\n' % addr @@ -368,10 +388,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # 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(self.case_i("Listen"), "443")) == 0: + 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" + self.server_root + "ports.conf", "Listen", "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" # Check for NameVirtualHost @@ -380,14 +401,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): tup = addr.partition(":") if tup[0] == "_default_": if not self.is_name_vhost(default_addr): - logger.debug("Setting all VirtualHosts on " + default_addr + " to be name based virtual hosts") + logger.debug(("Setting all VirtualHosts on " + "%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): - logger.debug("Setting VirtualHost at" + addr + "to be a name based virtual host") + logger.debug(("Setting VirtualHost at %s " + "to be a name based virtual host" % addr)) self.add_name_vhost(addr) return True @@ -397,11 +420,13 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Returns the path to . Creates the block if it does not exist """ - ifMods = self.aug.match(aug_conf_path + "/IfModule/*[self::arg='" + mod + "']") + ifMods = self.aug.match(("%s/IfModule/*[self::arg='%s']" % + (aug_conf_path, mod))) if len(ifMods) == 0: self.aug.set("%s/IfModule[last() + 1]" % aug_conf_path, "") self.aug.set("%s/IfModule[last()]/arg" % aug_conf_path, mod) - ifMods = self.aug.match("%s/IfModule/*[self::arg='%s']" % (aug_conf_path, mod)) + ifMods = self.aug.match(("%s/IfModule/*[self::arg='%s']" % + (aug_conf_path, mod))) # Strip off "arg" at end of first ifmod path return ifMods[0][:len(ifMods[0]) - 3] @@ -415,8 +440,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): self.aug.set(aug_conf_path + "/directive[last()]/arg", arg) else: for i in range(len(arg)): - self.aug.set(aug_conf_path + "/directive[last()]/arg["+str(i+1)+"]", arg[i]) - + self.aug.set("%s/directive[last()]/arg[%d]" % + (aug_conf_path, (i+1)), + arg[i]) def find_directive(self, directive, arg=None, start=""): """ @@ -436,51 +462,38 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if not start: start = "/files%sapache2.conf" % self.server_root - #Debug code - #print "find_dir:", directive, "arg:", arg, " | Looking in:", start + # Debug code + # print "find_dir:", directive, "arg:", arg, " | Looking in:", start # No regexp code # if arg is None: - # matches = self.aug.match(start + "//*[self::directive='"+directive+"']/arg") + # matches = self.aug.match(start + + # "//*[self::directive='"+directive+"']/arg") # else: - # matches = self.aug.match(start + "//*[self::directive='" + directive+"']/* [self::arg='" + arg + "']") + # matches = self.aug.match(start + + # "//*[self::directive='" + directive+"']/* [self::arg='" + arg + "']") - # includes = self.aug.match(start + "//* [self::directive='Include']/* [label()='arg']") + # includes = self.aug.match(start + + # "//* [self::directive='Include']/* [label()='arg']") if arg is None: - matches = self.aug.match(start + "//*[self::directive=~regexp('%s')]/arg" % directive) + ms = self.aug.match(("%s//*[self::directive=~regexp('%s')]/arg" % + (start, directive))) else: - matches = self.aug.match(start + "//*[self::directive=~regexp('%s')]/*[self::arg=~regexp('%s')]" % (directive, arg)) + ms = self.aug.match(("%s//*[self::directive=~regexp('%s')]/*" + "[self::arg=~regexp('%s')]" % + (start, directive, arg))) - includes = self.aug.match(start + "//* [self::directive=~regexp('%s')]/* [label()='arg']" % self.case_i('Include')) + includes = self.aug.match(("%s//* [self::directive=~regexp('%s')]/* " + "[label()='arg']" % + (start, case_i('Include')))) for include in includes: # start[6:] to strip off /files - matches.extend(self.find_directive(directive, arg, self.get_include_path(self.strip_dir(start[6:]), self.aug.get(include)))) + ms.extend(self.find_directive( + directive, arg, self.get_include_path(strip_dir(start[6:]), + self.aug.get(include)))) - return matches - - def case_i(self, string): - """ - Returns a sloppy, but necessary version of a case insensitive regex. - Any string should be able to be submitted and the string is - escaped and then made case insensitive. - May be replaced by a more proper /i once augeas 1.0 is widely - supported. - """ - - return "".join(["["+c.upper()+c.lower()+"]" if c.isalpha() else c for c in re.escape(string)]) - - def strip_dir(self, path): - """ - Precondition: file_path is a file path, ie. not an augeas section - or directive path - Returns the current directory from a file_path along with the file - """ - index = path.rfind("/") - if index > 0: - return path[:index+1] - # No directory - return "" + return ms def get_include_path(self, cur_dir, arg): """ @@ -499,7 +512,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # check_config to validate apache config doesn't work because it # would create a race condition between the check and this input - # TODO: Fix this + # TODO: Maybe... although I am convinced we have lost if + # Apache files can't be trusted. The augeas include path + # should be made to be exact. + # Check to make sure only expected characters are used <- maybe remove # validChars = re.compile("[a-zA-Z0-9.*?_-/]*") # matchObj = validChars.match(arg) @@ -529,7 +545,8 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): if "*" in split or "?" in split: # Turn it into a augeas regex # TODO: Can this instead be an augeas glob instead of regex - splitArg[idx] = "* [label()=~regexp('%s')]" % self.fnmatch_to_re(split) + splitArg[idx] = ("* [label()=~regexp('%s')]" % + self.fnmatch_to_re(split)) # Reassemble the argument arg = "/".join(splitArg) @@ -543,8 +560,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): Checks apache2ctl to get loaded module list """ try: - #p = subprocess.check_output(['sudo', '/usr/sbin/apache2ctl', '-M'], stderr=open("/dev/null", 'w')) - p = subprocess.Popen(['sudo', '/usr/sbin/apache2ctl', '-M'], stdout=subprocess.PIPE, stderr=open("/dev/null", 'w')).communicate()[0] + # p=subprocess.check_output(['sudo', '/usr/sbin/apache2ctl', '-M'], + # stderr=open("/dev/null", 'w')) + p = subprocess.Popen(['sudo', '/usr/sbin/apache2ctl', '-M'], + stdout=subprocess.PIPE, + stderr=open( + "/dev/null", 'w')).communicate()[0] except: logger.error("Error accessing apache2ctl for loaded modules!") logger.error("This may be caused by an Apache Configuration Error") @@ -590,8 +611,11 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # change address to address:443, address:80 addr_match = "/files%s//* [label()=~regexp('%s')]/arg" - ssl_addr_p = self.aug.match(addr_match % (ssl_fp, self.case_i('VirtualHost'))) - avail_addr_p = self.aug.match(addr_match % (avail_fp, self.case_i('VirtualHost'))) + ssl_addr_p = self.aug.match( + addr_match % (ssl_fp, case_i('VirtualHost'))) + avail_addr_p = self.aug.match( + 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]) @@ -605,13 +629,16 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): ssl_addrs.append(ssl_new_addr) # Add directives - vh_p = self.aug.match("/files%s//* [label()=~regexp('%s')]" % (ssl_fp, self.case_i('VirtualHost'))) + vh_p = self.aug.match(("/files%s//* [label()=~regexp('%s')]" % + (ssl_fp, case_i('VirtualHost')))) if len(vh_p) != 1: logger.error("Error: should only be one vhost in %s" % avail_fp) sys.exit(1) - self.add_dir(vh_p[0], "SSLCertificateFile", "/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], "SSLCertificateFile", + "/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) # Log actions and create save notes @@ -630,7 +657,9 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): # on after fully creating the new vhost need_to_save = False for i in range(len(nonssl_vhost.addrs)): - if self.is_name_vhost(nonssl_vhost.addrs[i]) and not self.is_name_vhost(ssl_addrs[i]): + + if (self.is_name_vhost(nonssl_vhost.addrs[i]) and + not self.is_name_vhost(ssl_addrs[i])): self.add_name_vhost(ssl_addrs[i]) logger.info("Enabling NameVirtualHosts on " + ssl_addrs[i]) need_to_save = True @@ -640,7 +669,6 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): return ssl_vhost - def enable_redirect(self, ssl_vhost): """ Adds Redirect directive to the port 80 equivalent of ssl_vhost @@ -648,13 +676,15 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): ip addresses that serves on non-ssl ports The function then adds the directive """ - # TODO - Enable check to see if it is already there to avoid extra restart + # TODO: Enable check to see if it is already there + # to avoid the extra restart self.enable_mod("rewrite") general_v = self.__general_vhost(ssl_vhost) if general_v is None: - #Add virtual_server with redirect - logger.debug("Did not find http version of ssl virtual host... creating") + # Add virtual_server with redirect + logger.debug( + "Did not find http version of ssl virtual host... creating") return self.create_redirect_vhost(ssl_vhost) else: # Check if redirection already exists @@ -666,10 +696,12 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): else: logger.debug("Unknown redirect exists for this vhost") return False, general_v - #Add directives to server + # Add directives to server self.add_dir(general_v.path, "RewriteEngine", "On") - 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) + 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)) self.save() return True, general_v @@ -685,8 +717,10 @@ class ApacheConfigurator(augeas_configurator.AugeasConfigurator): -1 is also returned in case of no redirection/rewrite directives """ - rewrite_path = self.find_directive(self.case_i("RewriteRule"), None, vhost.path) - redirect_path = self.find_directive(self.case_i("Redirect"), None, vhost.path) + rewrite_path = self.find_directive( + case_i("RewriteRule"), None, vhost.path) + redirect_path = self.find_directive( + case_i("Redirect"), None, vhost.path) if redirect_path: # "Existing Redirect directive for virtualhost" @@ -736,16 +770,17 @@ LogLevel warn \n\ # Write out the file # This is the default name - redirect_filename = "letsencrypt-redirect.conf" + redirect_filename = "le-redirect.conf" # See if a more appropriate name can be applied if len(ssl_vhost.names) > 0: # Sanity check... # make sure servername doesn't exceed filename length restriction if ssl_vhost.names[0] < (255-23): - redirect_filename = "letsencrypt-redirect-" + ssl_vhost.names[0] + ".conf" + redirect_filename = "le-redirect-%s.conf" % ssl_vhost.names[0] - redirect_filepath = self.server_root + "sites-available/" + redirect_filename + redirect_filepath = ("%ssites-available/%s" % + (self.server_root, redirect_filename)) # Register the new file that will be created # Note: always register the creation before writing to ensure file will @@ -764,7 +799,9 @@ LogLevel warn \n\ 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) + self.save_notes += ('Created a port 80 vhost, %s, for redirection to ' + 'ssl vhost %s\n' % + (new_vhost.file, ssl_vhost.file)) return True, new_vhost @@ -827,12 +864,15 @@ LogLevel warn \n\ test_tup = test_a.partition(":") if test_tup[0] == ssl_tup[0]: # Check if found... - if test_tup[2] == "80" or test_tup[2] == "" or test_tup[2] == "*": + if (test_tup[2] == "80" or + test_tup[2] == "" or + test_tup[2] == "*"): found += 1 break # Check to make sure all addresses were found # and names are equal - if found == len(ssl_vhost.addrs) and set(vh.names) == set(ssl_vhost.names): + if (found == len(ssl_vhost.addrs) and + set(vh.names) == set(ssl_vhost.names)): return vh return None @@ -848,21 +888,24 @@ LogLevel warn \n\ Retrieve all certs and keys set in VirtualHosts on the Apache server returns: list of tuples with form [(cert, key, path)] """ - c_k = set() + c_k = set() for vhost in self.vhosts: if vhost.ssl: - cert_path = self.find_directive(self.case_i("SSLCertificateFile"), None, vhost.path) - key_path = self.find_directive(self.case_i("SSLCertificateKeyFile"), None, vhost.path) + cert_path = self.find_directive( + case_i("SSLCertificateFile"), None, vhost.path) + key_path = self.find_directive( + case_i("SSLCertificateKeyFile"), None, vhost.path) # 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) + logger.error(("Too many cert or key directives in vhost " + "%s" % vhost.file)) sys.exit(40) cert = os.path.abspath(self.aug.get(cert_path[0])) key = os.path.abspath(self.aug.get(key_path[0])) - c_k.add( (cert, key, self.get_file_path(cert_path[0])) ) + c_k.add((cert, key, self.get_file_path(cert_path[0]))) return c_k @@ -877,7 +920,7 @@ LogLevel warn \n\ while True: # Cast both to lowercase to be case insensitive find_if = avail_fp.lower().find("/ifmodule") - if find_if != -1: + if find_if != -1: avail_fp = avail_fp[:find_if] continue find_vh = avail_fp.lower().find("/virtualhost") @@ -910,7 +953,8 @@ LogLevel warn \n\ return True if "/sites-available/" in vhost.file: - enabled_path = "%ssites-enabled/%s" % (self.server_root, os.path.basename(vhost.file)) + enabled_path = ("%ssites-enabled/%s" % + (self.server_root, os.path.basename(vhost.file))) self.register_file_creation(False, enabled_path) os.symlink(vhost.file, enabled_path) vhost.enabled = True @@ -925,9 +969,13 @@ LogLevel warn \n\ """ try: # Use check_output so the command will finish before reloading - subprocess.check_call(["sudo", "a2enmod", mod_name], stdout=open("/dev/null", 'w'), stderr=open("/dev/null", 'w')) + subprocess.check_call(["sudo", "a2enmod", 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"], stdout=open("/dev/null", 'w'), stderr=open("/dev/null", 'w')) + subprocess.check_call(["sudo", "/etc/init.d/apache2", "restart"], + stdout=open("/dev/null", 'w'), + stderr=open("/dev/null", 'w')) except Exception as e: logger.error("Error enabling mod_" + mod_name) logger.error("Exception: %s" % str(e)) @@ -958,19 +1006,21 @@ LogLevel warn \n\ """ # Test if augeas included file for Httpd.lens # Note: This works for augeas globs, ie. *.conf - incTest = self.aug.match("/augeas/load/Httpd/incl [. ='" + file_path + "']") + incTest = self.aug.match( + "/augeas/load/Httpd/incl [. ='%s']" % file_path) if not incTest: # Load up files - #self.httpd_incl.append(file_path) - #self.aug.add_transform("Httpd.lns", self.httpd_incl, None, self.httpd_excl) + # self.httpd_incl.append(file_path) + # self.aug.add_transform("Httpd.lns", + # self.httpd_incl, None, self.httpd_excl) self.__add_httpd_transform(file_path) self.aug.load() def save_apache_config(self): # Not currently used # Should be safe because it is a protected directory - shutil.copytree(self.server_root, CONFIG.BACKUP_DIR + "apache2-" + str(time.time())) - + shutil.copytree(self.server_root, + "%sapache2-%s" % (CONFIG.BACKUP_DIR, str(time.time()))) def verify_setup(self): ''' @@ -996,7 +1046,15 @@ LogLevel warn \n\ # I had no luck # This is a hack... work around... submit to augeas if still not fixed - excl = ["*.augnew", "*.augsave", "*.dpkg-dist", "*.dpkg-bak", "*.dpkg-new", "*.dpkg-old", "*.rpmsave", "*.rpmnew", "*~", self.server_root + "*.augsave", self.server_root + "*~", self.server_root + "*/*augsave", self.server_root + "*/*~", self.server_root + "*/*/*.augsave", self.server_root + "*/*/*~"] + excl = ["*.augnew", "*.augsave", "*.dpkg-dist", "*.dpkg-bak", + "*.dpkg-new", "*.dpkg-old", "*.rpmsave", "*.rpmnew", + "*~", + self.server_root + "*.augsave", + self.server_root + "*~", + self.server_root + "*/*augsave", + self.server_root + "*/*~", + self.server_root + "*/*/*.augsave", + self.server_root + "*/*/*~"] for i in range(len(excl)): self.aug.set("/augeas/load/Httpd/excl[%d]" % (i+1), excl[i]) @@ -1007,12 +1065,13 @@ LogLevel warn \n\ """ Restarts apache server """ - #TODO: This should be written to use the process returncode + # TODO: This should be written to use the process returncode try: - p = subprocess.Popen(['/etc/init.d/apache2', 'restart'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen(['/etc/init.d/apache2', 'restart'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) text = p.communicate() - if p.returncode != 0: # Enter recovery routine... logger.error("Configtest failed") @@ -1021,7 +1080,8 @@ LogLevel warn \n\ return False except: - logger.fatal("Apache Restart Failed - Please Check the Configuration") + logger.fatal(("Apache Restart Failed - " + "Please Check the Configuration")) sys.exit(1) return True @@ -1037,7 +1097,10 @@ LogLevel warn \n\ def config_test(self): try: - p = subprocess.Popen(['sudo', '/usr/sbin/apache2ctl', 'configtest'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = subprocess.Popen( + ['sudo', '/usr/sbin/apache2ctl', 'configtest'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) text = p.communicate() except: logger.fatal("Unable to run /usr/sbin/apache2ctl configtest") @@ -1085,7 +1148,8 @@ LogLevel warn \n\ for tup in chall_dict["listSNITuple"]: vhost = self.choose_virtual_host(tup[0]) if vhost is None: - logger.error("No vhost exists with servername or alias of:%s" % tup[0]) + logger.error(("No vhost exists with servername " + "or alias of: %s" % tup[0])) logger.error("No _default_:443 vhost exists") logger.error("Please specify servernames in the Apache config") return None @@ -1108,22 +1172,24 @@ LogLevel warn \n\ # Need to decode from base64 r = le_util.jose_b64decode(t[1]) ext = self.dvsni_gen_ext(r, s) - self.dvsni_create_chall_cert(t[0], ext, t[2], chall_dict["dvsni_key"]) + self.dvsni_create_chall_cert( + t[0], ext, t[2], chall_dict["dvsni_key"]) - self.dvsni_mod_config(self.user_config_file, chall_dict["listSNITuple"], - chall_dict["dvsni_key"], addresses) + self.dvsni_mod_config(self.user_config_file, + chall_dict["listSNITuple"], + chall_dict["dvsni_key"], + addresses) # Save reversible changes and restart the server self.save("SNI Challenge", True) self.restart(True) s = le_util.jose_b64encode(s) - return {"type":"dvsni", "s":s} + return {"type": "dvsni", "s": s} def cleanup(self): self.revert_challenge_config() self.restart(True) - def dvsni_get_cert_file(self, nonce): """ Returns standardized name for challenge certificate @@ -1163,12 +1229,15 @@ DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n \ """ Modifies Apache config files to include the challenge virtual servers - mainConfig: string - file path to Apache user config file + param mainConfig: file path to Apache user config file + type mainConfig: string + listSNITuple: list of tuples with form (addr, y, nonce, ext_oid) - addr (string), y (byte array), nonce (hex string), ext_oid (string) + addr (string), y (byte array), nonce (hex string), + ext_oid (string) key: string - file path to key - result: Apache config includes virtual servers for issued challenges + result: Apache config includes virtual servers for issued challs """ # Check to make sure options-ssl.conf is installed @@ -1180,7 +1249,9 @@ DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n \ # TODO: Use ip address of existing vhost instead of relying on FQDN configText = " \n" for idx, lis in enumerate(listlistAddrs): - configText += self.__getConfigText(listSNITuple[idx][2], lis, dvsni_key) + configText += self.__getConfigText(listSNITuple[idx][2], + lis, + dvsni_key) configText += " \n" self.dvsni_conf_include_check(mainConfig) @@ -1189,8 +1260,6 @@ DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n \ newConf.write(configText) newConf.close() - - def dvsni_conf_include_check(self, mainConfig): """ Adds DVSNI challenge include file if it does not already exist @@ -1198,15 +1267,17 @@ DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n \ mainConfig: string - file path to main user apache config file - result: User Apache configuration includes chocolate sni challenge file + result: User Apache configuration includes chocolate sni challenge file """ - if len(self.find_directive(self.case_i("Include"), CONFIG.APACHE_CHALLENGE_CONF)) == 0: - #print "Including challenge virtual host(s)" - self.add_dir("/files" + mainConfig, "Include", CONFIG.APACHE_CHALLENGE_CONF) + if len(self.find_directive( + case_i("Include"), CONFIG.APACHE_CHALLENGE_CONF)) == 0: + # print "Including challenge virtual host(s)" + self.add_dir("/files" + mainConfig, + "Include", CONFIG.APACHE_CHALLENGE_CONF) def dvsni_create_chall_cert(self, name, ext, nonce, key): """ - Modifies challenge certificate configuration and calls openssl binary to create a certificate + Modifies challenge certificate configuration and creates challenge cert ext: string - hex z value nonce: string - hex @@ -1216,7 +1287,10 @@ DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n \ """ self.register_file_creation(True, self.dvsni_get_cert_file(nonce)) - cert_pem = crypto_util.make_ss_cert(key, [nonce + CONFIG.INVALID_EXT, name, ext]) + + cert_pem = crypto_util.make_ss_cert( + key, [nonce + CONFIG.INVALID_EXT, name, ext]) + with open(self.dvsni_get_cert_file(nonce), 'w') as f: f.write(cert_pem) @@ -1235,6 +1309,33 @@ DocumentRoot " + CONFIG.CONFIG_DIR + "challenge_page/ \n \ return h.hexdigest() + CONFIG.INVALID_EXT + +def case_i(string): + """ + Returns a sloppy, but necessary version of a case insensitive regex. + Any string should be able to be submitted and the string is + escaped and then made case insensitive. + May be replaced by a more proper /i once augeas 1.0 is widely + supported. + """ + + return "".join(["["+c.upper()+c.lower()+"]" + if c.isalpha() else c for c in re.escape(string)]) + + +def strip_dir(path): + """ + Precondition: file_path is a file path, ie. not an augeas section + or directive path + Returns the current directory from a file_path along with the file + """ + index = path.rfind("/") + if index > 0: + return path[:index+1] + # No directory + return "" + + def main(): config = ApacheConfigurator() logger.setLogger(logger.FileLogger(sys.stdout)) @@ -1246,7 +1347,8 @@ def main(): for name in v.names: print name """ - print config.find_directive(config.case_i("NameVirtualHost"), config.case_i("holla:443")) + print config.find_directive( + config.case_i("NameVirtualHost"), config.case_i("holla:443")) """ for m in config.find_directive("Listen", "443"): @@ -1262,18 +1364,18 @@ def main(): test_file = "/home/james/Desktop/ports_test.conf" config.parse_file(test_file) - config.aug.insert("/files" + test_file + "/IfModule[1]/arg", "directive", False) - config.aug.set("/files" + test_file + "/IfModule[1]/directive[1]", "Listen") - config.aug.set("/files" + test_file + "/IfModule[1]/directive[1]/arg", "556") - config.aug.set("/files" + test_file + "/IfModule[1]/directive[2]", "Listen") - config.aug.set("/files" + test_file + "/IfModule[1]/directive[2]/arg", "555") + config.aug.insert("/files"+test_file+"/IfModule[1]/arg","directive",False) + config.aug.set("/files" +test_file+ "/IfModule[1]/directive[1]", "Listen") + config.aug.set("/files" +test_file+ "/IfModule[1]/directive[1]/arg", "556") + config.aug.set("/files" +test_file+ "/IfModule[1]/directive[2]", "Listen") + config.aug.set("/files" +test_file+ "/IfModule[1]/directive[2]/arg", "555") #config.save_notes = "Added listen 431 for test" #config.register_file_creation("/home/james/Desktop/new_file.txt") #config.save("Testing Saves", False) #config.recover_checkpoint(1) """ - #config.display_checkpoints() + # config.display_checkpoints() config.config_test() """ # Testing redirection and make_vhost_ssl @@ -1291,8 +1393,11 @@ def main(): """ for vh in config.vhosts: if len(vh.names) > 0: - config.deploy_cert(vh, "/home/james/Documents/apache_choc/req.pem", "/home/james/Documents/apache_choc/key.pem", "/home/james/Downloads/sub.class1.server.ca.pem") - """ + config.deploy_cert(vh, + "/home/james/Documents/apache_choc/req.pem", + "/home/james/Documents/apache_choc/key.pem", + "/home/james/Downloads/sub.class1.server.ca.pem") + """ if __name__ == "__main__": main() diff --git a/letsencrypt/client/client.py b/letsencrypt/client/client.py index b4dc5542d..d4029e891 100644 --- a/letsencrypt/client/client.py +++ b/letsencrypt/client/client.py @@ -82,10 +82,6 @@ class Client(object): else: sys.exit(0) - # Display choice of CA screen - # TODO: Use correct server depending on CA - #choice = self.choice_of_ca() - # Request Challenges challenge_msg = self.acme_challenge()