def _mod_config(self): """Modifies Nginx config to include server_names_hash_bucket_size directive and server challenge blocks. :raises .MisconfigurationError: Unable to find a suitable HTTP block in which to include authenticator hosts. """ included = False include_directive = ['\n', 'include', ' ', self.challenge_conf] root = self.configurator.parser.config_root bucket_directive = ['\n', 'server_names_hash_bucket_size', ' ', '128'] main = self.configurator.parser.parsed[root] for line in main: if line[0] == ['http']: body = line[1] found_bucket = False posn = 0 for inner_line in body: if inner_line[0] == bucket_directive[1]: if int(inner_line[1]) < int(bucket_directive[3]): body[posn] = bucket_directive found_bucket = True posn += 1 if not found_bucket: body.insert(0, bucket_directive) if include_directive not in body: body.insert(0, include_directive) included = True break if not included: raise errors.MisconfigurationError( 'Certbot could not find a block to include ' 'challenges in %s.' % root) config = [self._make_or_mod_server_block(achall) for achall in self.achalls] config = [x for x in config if x is not None] config = nginxparser.UnspacedList(config) logger.debug("Generated server block:\n%s", str(config)) self.configurator.reverter.register_file_creation( True, self.challenge_conf) with open(self.challenge_conf, "w") as new_conf: nginxparser.dump(config, new_conf)
def _test_block_from_block(block): test_block = nginxparser.UnspacedList(block) parser.comment_directive(test_block, 0) return test_block[:-1]
def _update_or_add_directives(directives, insert_at_top, block): """Adds or replaces directives in a config block.""" for directive in directives: _update_or_add_directive(block, directive, insert_at_top) if block and '\n' not in block[-1]: # could be " \n " or ["\n"] ! block.append(nginxparser.UnspacedList('\n'))
def _mod_config(self) -> None: """Modifies Nginx config to include server_names_hash_bucket_size directive and server challenge blocks. :raises .MisconfigurationError: Unable to find a suitable HTTP block in which to include authenticator hosts. """ included = False include_directive = ['\n', 'include', ' ', self.challenge_conf] root = self.configurator.parser.config_root bucket_directive = ['\n', 'server_names_hash_bucket_size', ' ', '128'] main = self.configurator.parser.parsed[root] # insert include directive for line in main: if line[0] == ['http']: body = line[1] if include_directive not in body: body.insert(0, include_directive) included = True break # insert or update the server_names_hash_bucket_size directive # We have several options here. # 1) Only check nginx.conf # 2) Check included files, assuming they've been included inside http already, # because if they added it outside an http block their config is broken anyway # 3) Add metadata during parsing to note if an include happened inside the http block # # 1 causes bugs; see https://github.com/certbot/certbot/issues/5199 # 3 would require a more extensive rewrite and probably isn't necessary anyway # So this code uses option 2. found_bucket = False for file_contents in self.configurator.parser.parsed.values(): body = file_contents # already inside http in an included file for line in file_contents: if line[0] == ['http']: body = line[1] # enter http because this is nginx.conf break for posn, inner_line in enumerate(body): if inner_line[0] == bucket_directive[1]: if int(inner_line[1]) < int(bucket_directive[3]): body[posn] = bucket_directive found_bucket = True break if found_bucket: break if not found_bucket: for line in main: if line[0] == ['http']: body = line[1] body.insert(0, bucket_directive) break if not included: raise errors.MisconfigurationError( 'Certbot could not find a block to include ' 'challenges in %s.' % root) config = [self._make_or_mod_server_block(achall) for achall in self.achalls] config = [x for x in config if x is not None] config = nginxparser.UnspacedList(config) logger.debug("Generated server block:\n%s", str(config)) self.configurator.reverter.register_file_creation( True, self.challenge_conf) with io.open(self.challenge_conf, "w", encoding="utf-8") as new_conf: nginxparser.dump(config, new_conf)
def _add_directive(block: UnspacedList, directive: Sequence[Any], insert_at_top: bool) -> None: if not isinstance(directive, nginxparser.UnspacedList): directive = nginxparser.UnspacedList(directive) if _is_whitespace_or_comment(directive): # whitespace or comment block.append(directive) return location = _find_location(block, directive[0]) # Append or prepend directive. Fail if the name is not a repeatable directive name, # and there is already a copy of that directive with a different value # in the config file. # handle flat include files directive_name = directive[0] def can_append(loc: Optional[int], dir_name: str) -> bool: """ Can we append this directive to the block? """ return loc is None or (isinstance(dir_name, str) and dir_name in REPEATABLE_DIRECTIVES) err_fmt = 'tried to insert directive "{0}" but found conflicting "{1}".' # Give a better error message about the specific directive than Nginx's "fail to restart" if directive_name == INCLUDE: # in theory, we might want to do this recursively, but in practice, that's really not # necessary because we know what file we're talking about (and if we don't recurse, we # just give a worse error message) included_directives = _parse_ssl_options(directive[1]) for included_directive in included_directives: included_dir_loc = _find_location(block, included_directive[0]) included_dir_name = included_directive[0] if (not _is_whitespace_or_comment(included_directive) and not can_append(included_dir_loc, included_dir_name)): # By construction of can_append(), included_dir_loc cannot be None at that point resolved_included_dir_loc = cast(int, included_dir_loc) if block[resolved_included_dir_loc] != included_directive: raise errors.MisconfigurationError( err_fmt.format(included_directive, block[resolved_included_dir_loc])) _comment_out_directive(block, resolved_included_dir_loc, directive[1]) if can_append(location, directive_name): if insert_at_top: # Add a newline so the comment doesn't comment # out existing directives block.insert(0, nginxparser.UnspacedList('\n')) block.insert(0, directive) comment_directive(block, 0) else: block.append(directive) comment_directive(block, len(block) - 1) return # By construction of can_append(), location cannot be None at that point resolved_location = cast(int, location) if block[resolved_location] != directive: raise errors.MisconfigurationError( err_fmt.format(directive, block[resolved_location]))