Beispiel #1
0
    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]
Beispiel #3
0
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'))
Beispiel #4
0
    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)
Beispiel #5
0
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]))