普通文本  |  94行  |  3.3 KB

# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import logging
import os

from autotest_lib.client.bin import test
from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import utils
from autotest_lib.client.common_lib.cros.tendo import webservd_helper


class security_Firewall(test.test):
    """Tests that rules in iptables/ip6tables match our expectations exactly."""
    version = 1


    @staticmethod
    def get_firewall_settings(executable):
        rules = utils.system_output("%s -S" % executable)
        return set([line.strip() for line in rules.splitlines()])


    def load_baseline(self, baseline_filename):
        """The baseline file lists the rules that we expect.

        @param baseline_filename: string name of file containing relevant rules.
        """
        baseline_path = os.path.join(self.bindir, baseline_filename)
        with open(baseline_path) as f:
            return set([line.strip() for line in f.readlines()])


    def dump_rules(self, rules, executable):
        """Store actual rules in results/ for future use.

        Leaves a list of iptables/ip6tables rules in the results dir
        so that we can update the baseline file if necessary.

        @param rules: list of string containing rules we found on the board.
        @param executable: 'iptables' for IPv4 or 'ip6tables' for IPv6.
        """
        outf = open(os.path.join(self.resultsdir, "%s_rules" % executable), 'w')
        for rule in rules:
            outf.write(rule + "\n")

        outf.close()


    @staticmethod
    def log_error_rules(rules, message):
        """Log a set of rules and the problem with those rules.

        @param rules: list of string containing rules we have issues with.
        @param message: string detailing what our problem with the rules is.
        """
        rules_str = ", ".join(["'%s'" % rule for rule in rules])
        logging.error("%s: %s", message, rules_str)


    def run_once(self):
        """Matches found and expected iptables/ip6tables rules.
        Fails only when rules are missing.
        """

        failed = False
        for executable in ["iptables", "ip6tables"]:
            baseline = self.load_baseline("baseline.%s" % executable)
            # TODO(wiley) Remove when we get per-board baselines (crbug.com/406013)
            webserv_rules = self.load_baseline("baseline.webservd")
            if webservd_helper.webservd_is_running():
                baseline.update(webserv_rules)
            current = self.get_firewall_settings(executable)

            # Save to results dir
            self.dump_rules(current, executable)

            missing_rules = baseline - current
            extra_rules = current - baseline

            if len(missing_rules) > 0:
                failed = True
                self.log_error_rules(missing_rules,
                                     "Missing %s rules" % executable)

            if len(extra_rules) > 0:
                # TODO(zqiu): implement a way to verify per-interface rules
                # that are created dynamically.
                self.log_error_rules(extra_rules, "Extra %s rules" % executable)

        if failed:
            raise error.TestFail("Mismatched firewall rules")