def test_commands(monkeypatch): """ . """ # pylint: disable=unused-argument @counter_wrapper def rule_commands(self) -> List[str]: commands = [] if rule_commands.counter == 1: commands = ["rule1-command1", "rule1-command2"] else: commands = ["rule2-command1", "rule2-command2", "rule2-command3"] return commands monkeypatch.setattr(NATRule, "commands", rule_commands) nat_properties = { "rules": [NATRule(10, "."), NATRule(20, ".")], } nat = NAT(".", **nat_properties) assert nat.commands() == [ "rule1-command1", "rule1-command2", "rule2-command1", "rule2-command2", "rule2-command3", ], "Commands generated correctly"
def test_masquerade(): """ Masquerade differs from normal rules, check that separately """ properties = { "type": "masquerade", "protocol": "all", "outbound-interface": "eth0" } rule = NATRule(1000, ".", **properties) assert rule.validate(), "Masquerade rule valid"
def test_get_next_number(): """ . """ properties = { "rules": [NATRule(11, "."), NATRule(100, ".")], "auto-increment": 100, } nat = NAT(".", **properties) assert getattr(nat, "auto-increment") == 100, "Auto increment overridden" assert nat.next_rule_number() == 200, "Next rule number correct" nat.add_rule({"number": 200, "config_path": "."}) assert nat.next_rule_number() == 300, "Next rule number updated after adding rule"
def test_group_address_commands(): """ . """ rule_properties = { "description": "rule", "source": { "address": "a-group", "port": "web-ports" }, "destination": { "address": "the.whole.internet", "port": "any" }, } commands = NATRule(10, ".", **rule_properties).commands() rule_base = "service nat rule 10 " assert commands == [ rule_base + 'description "rule"', rule_base + "source group address-group a-group", rule_base + "source group port-group web-ports", rule_base + "destination group address-group the.whole.internet", rule_base + "destination group port-group any", ], "Rule commands correct"
def test_non_group_address_commands(): """ . """ rule_properties = { "description": "rule", "source": { "address": "10.10.10.10", "port": 8080 }, "destination": { "address": "8.8.8.8", "port": "443" }, "inside-address": { "address": "192.168.0.2", "port": 80 }, } commands = NATRule(10, ".", **rule_properties).commands() rule_base = "service nat rule 10 " assert commands == [ rule_base + 'description "rule"', rule_base + "source address 10.10.10.10", rule_base + "source port 8080", rule_base + "destination address 8.8.8.8", rule_base + "destination port 443", rule_base + "inside-address address 192.168.0.2", rule_base + "inside-address port 80", ], "Rule commands correct"
def test_consistency(): """ . """ nat = NAT( ".", rules=[ NATRule(10, "."), NATRule(10, "."), NATRule(20, "."), NATRule(20, "."), NATRule(30, "."), ], ) assert not nat.is_consistent(), "Not consistent" assert nat.validation_errors() == ["NAT has duplicate rules: 10, 20"]
def test_validate(monkeypatch): """ . """ # pylint: disable=unused-argument @counter_wrapper def fake_validate(self): """ . """ return True monkeypatch.setattr(NATRule, "validate", fake_validate) nat = NAT(".", rules=[NATRule(1, "."), NATRule(2, ".")],) assert nat.validate(), "NAT is valid" assert fake_validate.counter == 2, "Validation called for each rule" monkeypatch.setattr(NATRule, "validate", lambda self: False) assert not nat.validate(), "Rule validation fails"
def test_validation_failures(monkeypatch): """ . """ def fake_validation_errors(self) -> List[str]: """ . """ return ["problem"] if self.number == 10 else ["bad", "wrong"] monkeypatch.setattr(NATRule, "validation_errors", fake_validation_errors) monkeypatch.setattr(NAT, "validation_errors", lambda self: ["an error"]) nat = NAT(".", rules=[NATRule(10, "."), NATRule(20, ".")],) assert nat.validation_failures() == [ "an error", "problem", "bad", "wrong", ], "Validation failures are correct"
def test_non_address_commands(): """ . """ rule_properties = { "description": "Its a rule", "log": "disable", "protocol": "tcp", "type": "source", "inbound-interface": "eth1.10", "outbound-interface": "eth0", } commands = NATRule(10, ".", **rule_properties).commands() rule_base = "service nat rule 10 " assert commands == [ rule_base + "description 'Its a rule'", rule_base + "log disable", rule_base + "protocol tcp", rule_base + "type source", rule_base + "inbound-interface eth1.10", rule_base + "outbound-interface eth0", ], "Rule commands correct"
def test_add_firewall_rules(): """ . """ host_properties = { "address-groups": ["devices"], "forward-ports": [ 80, "443", "server-ports", {"8080": 80}, {4443: 443}, {222: "22"}, ], "hairpin-ports": [ { "connection": {"destination": {"address": "8.8.8.8", "port": 53}}, "interface": "eth1.10", }, { "connection": {"destination": {"port": "server-ports"}}, "description": "Hairpin server ports back to host", "interface": "eth0", }, ], "connections": [ { "allow": True, "rule": 20, "log": False, "description": "A rule", "source": {"address": "192.168.0.0/24",}, "destination": {"port": "printer-ports"}, }, { "allow": False, "log": True, "description": "A rule", "source": {"address": "bad-things",}, "destination": {"address": "devices", "port": 22}, }, { "allow": True, "description": "A rule", "source": {"address": "devices",}, "destination": {"port": "allowed-ports"}, }, { "allow": False, "description": "A rule", "log": True, "source": {"address": "devices",}, "destination": {"port": "blocked-ports"}, }, { "allow": True, "description": "A rule", "log": False, "source": {"port": "allowed-ports",}, "destination": {"address": "devices"}, }, ], } host = Host( "host1", Network( "network", NAT("."), ".", "192.168.0.0/24", **{"interface-name": "eth0"} ), ".", "192.168.0.100", **host_properties ) firewall_in = host.network.firewalls_by_direction["in"] firewall_out = host.network.firewalls_by_direction["out"] rule_20 = { "number": 20, "firewall_name": "network-IN", "config_path": ".", "action": "accept", "protocol": "tcp_udp", "log": "disable", "description": "A rule", "source": {"address": "192.168.0.0/24"}, "destination": {"port": "printer-ports"}, } rule_10 = copy.deepcopy(rule_20) rule_10["number"] = 10 rule_10["source"]["address"] = "devices" rule_10["destination"]["port"] = "allowed-ports" rule_30 = copy.deepcopy(rule_10) rule_30["number"] = 30 rule_30["action"] = "drop" rule_30["log"] = "enable" rule_30["destination"]["port"] = "blocked-ports" assert firewall_in.rules == [ Rule(**rule_20), Rule(**rule_10), Rule(**rule_30), ], "Rules created for in firewall correctly" rule_10["action"] = "drop" rule_10["source"]["address"] = "bad-things" rule_10["destination"]["address"] = "devices" rule_10["destination"]["port"] = 22 rule_10["log"] = "enable" rule_20 = copy.deepcopy(rule_10) rule_20["number"] = 20 rule_20["action"] = "accept" rule_20["log"] = "disable" rule_20["source"] = {"port": "allowed-ports"} rule_20["destination"] = {"address": "devices"} assert firewall_out.rules == [ Rule(**rule_10), Rule(**rule_20), ], "Rules created for out firewall correctly" base_rule_properties = { "number": 10, "description": "Forward port 80 to host1", "config_path": ".", "type": "destination", "protocol": "tcp_udp", "inbound-interface": "eth0", "inside-address": {"address": "192.168.0.100"}, "destination": {"port": 80}, } ssl_rule = copy.deepcopy(base_rule_properties) ssl_rule["number"] = 20 ssl_rule["destination"]["port"] = "443" ssl_rule["description"] = ssl_rule["description"].replace("80", "443") server_rule = copy.deepcopy(base_rule_properties) server_rule["number"] = 30 server_rule["destination"]["port"] = "server-ports" server_rule["description"] = server_rule["description"].replace( "80", "server-ports" ) str_translate_rule = copy.deepcopy(base_rule_properties) str_translate_rule["number"] = 40 str_translate_rule["destination"]["port"] = "8080" str_translate_rule["inside-address"]["port"] = 80 str_translate_rule["description"] = str_translate_rule["description"].replace( "80", "8080" ) int_translate_rule = copy.deepcopy(base_rule_properties) int_translate_rule["number"] = 50 int_translate_rule["destination"]["port"] = 4443 int_translate_rule["inside-address"]["port"] = 443 int_translate_rule["description"] = int_translate_rule["description"].replace( "80", "4443" ) other_translate_rule = copy.deepcopy(base_rule_properties) other_translate_rule["number"] = 60 other_translate_rule["destination"]["port"] = 222 other_translate_rule["inside-address"]["port"] = "22" other_translate_rule["description"] = other_translate_rule["description"].replace( "80", "222" ) hairpin_rule = { "number": 70, "description": "", "config_path": ".", "type": "destination", "protocol": "tcp_udp", "inside-address": {"address": "192.168.0.100"}, "destination": {"address": "8.8.8.8", "port": 53}, "inbound-interface": "eth1.10", } hairpin_rule_2 = copy.deepcopy(hairpin_rule) hairpin_rule_2["number"] = 80 hairpin_rule_2["description"] = "Hairpin server ports back to host" hairpin_rule_2["destination"] = { "address": "external-addresses", "port": "server-ports", } hairpin_rule_2["inbound-interface"] = "eth0" assert host.network.nat.rules == [ NATRule(**base_rule_properties), NATRule(**ssl_rule), NATRule(**server_rule), NATRule(**str_translate_rule), NATRule(**int_translate_rule), NATRule(**other_translate_rule), NATRule(**hairpin_rule), NATRule(**hairpin_rule_2), ], "NAT rules created"
def test_validate(monkeypatch): """ . """ # pylint: disable=unused-argument @counter_wrapper def fake_validate_false(self) -> bool: """ . """ return False @counter_wrapper def fake_validate_true(self) -> bool: """ . """ return True monkeypatch.setattr( secondary_configs, "get_port_groups", lambda config_path: [PortGroup("group1"), PortGroup("group2")], ) monkeypatch.setattr(Validatable, "validate", fake_validate_false) rule = NATRule(10, ".") assert not rule.validate(), "Validation fails if parent fails" assert fake_validate_false.counter == 1, "Parent validation called" monkeypatch.setattr(Validatable, "validate", fake_validate_true) rule = NATRule(10, ".") assert not rule.validate(), "Validation fails without inside address" assert fake_validate_true.counter == 1, "Parent validation called" assert rule.validation_errors() == ["NAT rule 10 does not have type" ], "Validation errors set" properties = { "source": { "port": "group1" }, "destination": { "port": "group2" }, } rule = NATRule(10, ".", **properties) assert not rule.validate(), "Missing type invalid" assert rule.validation_errors() == ["NAT rule 10 does not have type" ], "Errors set" properties["type"] = "source" rule = NATRule(10, ".", **properties) assert not rule.validate(), "Missing type invalid" assert rule.validation_errors() == [ "NAT rule 10 does not have inside address" ], "Errors set" properties["inside-address"] = ({"address": "192.168.0.2"}, ) rule = NATRule(10, ".", **properties) assert rule.validate(), "Rule is valid if groups are valid" rule = NATRule(10, ".", **properties) properties["source"]["port"] = "group3" properties["destination"]["port"] = "group4" assert not rule.validate(), "Rule is invalid with nonexistent groups" assert rule.validation_errors() == [ "NAT rule 10 has nonexistent source port group group3", "NAT rule 10 has nonexistent destination port group group4", ], "Errors added"
def test_string(): """ . """ rule = NATRule(123, ".") assert str(rule) == "NAT rule 123", "String correct"