Ejemplo n.º 1
0
 def test_negative_matches(self):
     self.index.on_labels_update("l0", {})
     self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
     self.index.on_labels_update("l2", {"a": "a2", "b": "b2"})
     self.index.on_labels_update("l3", {"a": "a3", "c": "c3"})
     self.index.on_expression_update("not_a", parse_selector('a!="a"'))
     self.index.on_expression_update("not_d", parse_selector('d!="d"'))
     self.assert_add("not_d", "l0")
     self.assert_add("not_d", "l1")
     self.assert_add("not_d", "l2")
     self.assert_add("not_d", "l3")
     self.assert_add("not_a", "l0")
     self.assert_add("not_a", "l2")
     self.assert_add("not_a", "l3")
     self.assert_no_updates()
     self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
     self.assert_remove("not_a", "l2")
     self.assert_no_updates()
     self.index.on_labels_update("l1", {"a": "a", "d": "d1"})
     self.index.on_labels_update("l2", {"a": "a", "d": "d"})
     self.assert_remove("not_d", "l2")
     self.assert_no_updates()
     self.index.on_expression_update("not_a", None)
     self.index.on_expression_update("not_d", None)
     self.assert_remove("not_a", "l0")
     self.assert_remove("not_a", "l3")
     self.assert_remove("not_d", "l1")
     self.assert_remove("not_d", "l0")
     self.assert_remove("not_d", "l3")
     self.index.on_labels_update("l0", None)
     self.index.on_labels_update("l1", None)
     self.index.on_labels_update("l2", None)
     self.index.on_labels_update("l3", None)
     self.assert_indexes_empty()
Ejemplo n.º 2
0
 def test_mainline(self):
     # Add some labels.
     self.index.on_labels_update("l1", {"a": "a1", "b": "b1", "c": "c1"})
     self.assert_no_updates()
     # Add a non-matching expression
     self.index.on_expression_update("e1", parse_selector('d=="d1"'))
     self.assert_no_updates()
     # Add a matching expression
     self.index.on_expression_update("e2", parse_selector('a=="a1"'))
     self.assert_add("e2", "l1")
     self.assert_no_updates()
     # Update matching expression, still matches
     self.index.on_expression_update("e2", parse_selector('b=="b1"'))
     self.assert_no_updates()
     # Update matching expression, no-longer matches
     self.index.on_expression_update("e2", parse_selector('b=="b2"'))
     self.assert_remove("e2", "l1")
     self.assert_no_updates()
     # Update labels to match.
     self.index.on_labels_update("l1", {"b": "b2", "d": "d1"})
     self.assert_add("e1", "l1")
     self.assert_add("e2", "l1")
     self.assert_no_updates()
     self.index.on_labels_update("l1", None)
     self.assert_remove("e1", "l1")
     self.assert_remove("e2", "l1")
     self.index.on_expression_update("e1", None)
     self.index.on_expression_update("e2", None)
     self.assert_indexes_empty()
Ejemplo n.º 3
0
 def test_replace_selector_with_object(self):
     """
     Checks that the validate_profile() method replaces selectors
     (with their object representations).
     """
     policy = {
         "selector":
         "a == 'b'",
         "inbound_rules": [{
             "src_selector": "b == 'c'",
             "dst_selector": "e == 'f'"
         }],
         "outbound_rules": [{
             "src_selector": "h == 'c'",
             "dst_selector": "i == 'f'"
         }],
         "order":
         10
     }
     common.validate_policy(TieredPolicyId("a", "b"), policy)
     self.assertEqual(policy["selector"], parse_selector("a == 'b'"))
     self.assertEqual(policy["inbound_rules"][0]["src_selector"],
                      parse_selector("b == 'c'"))
     self.assertEqual(policy["inbound_rules"][0]["dst_selector"],
                      parse_selector("e == 'f'"))
     self.assertEqual(policy["outbound_rules"][0]["src_selector"],
                      parse_selector("h == 'c'"))
     self.assertEqual(policy["outbound_rules"][0]["dst_selector"],
                      parse_selector("i == 'f'"))
Ejemplo n.º 4
0
 def test_mainline(self):
     # Add some labels.
     self.index.on_labels_update("l1", {"a": "a1", "b": "b1", "c": "c1"})
     self.assert_no_updates()
     # Add a non-matching expression
     self.index.on_expression_update("e1", parse_selector('d=="d1"'))
     self.assert_no_updates()
     # Add a matching expression
     self.index.on_expression_update("e2", parse_selector('a=="a1"'))
     self.assert_add("e2", "l1")
     self.assert_no_updates()
     # Update matching expression, still matches
     self.index.on_expression_update("e2", parse_selector('b=="b1"'))
     self.assert_no_updates()
     # Update matching expression, no-longer matches
     self.index.on_expression_update("e2", parse_selector('b=="b2"'))
     self.assert_remove("e2", "l1")
     self.assert_no_updates()
     # Update labels to match.
     self.index.on_labels_update("l1", {"b": "b2", "d": "d1"})
     self.assert_add("e1", "l1")
     self.assert_add("e2", "l1")
     self.assert_no_updates()
     self.index.on_labels_update("l1", None)
     self.assert_remove("e1", "l1")
     self.assert_remove("e2", "l1")
     self.index.on_expression_update("e1", None)
     self.index.on_expression_update("e2", None)
     self.assert_indexes_empty()
Ejemplo n.º 5
0
    def test_multiple_matches(self):
        # Insert some labels.
        self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
        self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
        self.index.on_labels_update("l3", {"a": "a", "b": "b3"})
        self.index.on_labels_update("l4", {"c": "c1"})
        # And some expressions.
        self.index.on_expression_update("e1", parse_selector('a=="a"'))
        # e2 starts off matching l4, which only has index entries for c==c1.
        # This ensures that we hit the cleanup code when we change e2 because
        # the the indexes that apply to the new value of e2 don't apply to
        # l4.
        self.index.on_expression_update("e2", parse_selector('c=="c1"'))
        self.assert_add("e2", "l4")
        self.index.on_expression_update("e2",
                                        parse_selector('a=="a" && b=="b1"'))
        self.assert_remove("e2", "l4")
        self.assert_add("e1", "l1")
        self.assert_add("e1", "l2")
        self.assert_add("e1", "l3")
        self.assert_add("e2", "l1")
        self.assert_no_updates()

        self.index.on_expression_update("e1", parse_selector('a=="z"'))
        self.assert_remove("e1", "l1")
        self.assert_remove("e1", "l2")
        self.assert_remove("e1", "l3")
        self.assert_no_updates()
        self.index.on_expression_update("e1", None)
        self.assert_no_updates()
        self.index.on_expression_update("e2", None)
        self.assert_remove("e2", "l1")
        self.index.on_expression_update("e4", None)
        self.assert_no_updates()
Ejemplo n.º 6
0
 def test_multiple_matches(self):
     # Insert some labels.
     self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
     self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
     self.index.on_labels_update("l3", {"a": "a", "b": "b3"})
     self.index.on_labels_update("l4", {"c": "c1"})
     # And some expressions.
     self.index.on_expression_update("e1", parse_selector('a=="a"'))
     # e2 starts off matching l4, which only has index entries for c==c1.
     # This ensures that we hit the cleanup code when we change e2 because
     # the the indexes that apply to the new value of e2 don't apply to
     # l4.
     self.index.on_expression_update("e2", parse_selector('c=="c1"'))
     self.assert_add("e2", "l4")
     self.index.on_expression_update("e2",
                                     parse_selector('a=="a" && b=="b1"'))
     self.assert_remove("e2", "l4")
     self.assert_add("e1", "l1")
     self.assert_add("e1", "l2")
     self.assert_add("e1", "l3")
     self.assert_add("e2", "l1")
     self.assert_no_updates()
     
     self.index.on_expression_update("e1", parse_selector('a=="z"'))
     self.assert_remove("e1", "l1")
     self.assert_remove("e1", "l2")
     self.assert_remove("e1", "l3")
     self.assert_no_updates()
     self.index.on_expression_update("e1", None)
     self.assert_no_updates()
     self.index.on_expression_update("e2", None)
     self.assert_remove("e2", "l1")
     self.index.on_expression_update("e4", None)
     self.assert_no_updates()
Ejemplo n.º 7
0
 def test_negative_matches(self):
     self.index.on_labels_update("l0", {})
     self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
     self.index.on_labels_update("l2", {"a": "a2", "b": "b2"})
     self.index.on_labels_update("l3", {"a": "a3", "c": "c3"})
     self.index.on_expression_update("not_a", parse_selector('a!="a"'))
     self.index.on_expression_update("not_d", parse_selector('d!="d"'))
     self.assert_add("not_d", "l0")
     self.assert_add("not_d", "l1")
     self.assert_add("not_d", "l2")
     self.assert_add("not_d", "l3")
     self.assert_add("not_a", "l0")
     self.assert_add("not_a", "l2")
     self.assert_add("not_a", "l3")
     self.assert_no_updates()
     self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
     self.assert_remove("not_a", "l2")
     self.assert_no_updates()
     self.index.on_labels_update("l1", {"a": "a", "d": "d1"})
     self.index.on_labels_update("l2", {"a": "a", "d": "d"})
     self.assert_remove("not_d", "l2")
     self.assert_no_updates()
     self.index.on_expression_update("not_a", None)
     self.index.on_expression_update("not_d", None)
     self.assert_remove("not_a", "l0")
     self.assert_remove("not_a", "l3")
     self.assert_remove("not_d", "l1")
     self.assert_remove("not_d", "l0")
     self.assert_remove("not_d", "l3")
     self.index.on_labels_update("l0", None)
     self.index.on_labels_update("l1", None)
     self.index.on_labels_update("l2", None)
     self.index.on_labels_update("l3", None)
     self.assert_indexes_empty()
Ejemplo n.º 8
0
    def test_set_indexing(self):
        self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
        self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
        self.index.on_labels_update("l3", {"a": "a2", "b": "b3"})

        # Add an expression with existing labels.
        self.index.on_expression_update("e1", parse_selector("a in {'a', 'z'}"))
        self.assert_add("e1", "l1")
        self.assert_add("e1", "l2")

        # And one that uses a real set.
        self.index.on_expression_update("e2", parse_selector("b in {'b1', 'b2'}"))
        self.assert_add("e2", "l1")
        self.assert_add("e2", "l2")
        self.assert_no_updates()

        # Then update them.
        self.index.on_expression_update("e2", parse_selector("b in {'b2', 'b3'}"))
        self.assert_add("e2", "l3")
        self.assert_remove("e2", "l1")
        self.assert_no_updates()

        self.index.on_expression_update("e1", parse_selector("a in {'a2'}"))
        self.assert_add("e1", "l3")
        self.assert_remove("e1", "l1")
        self.assert_remove("e1", "l2")
        self.assert_no_updates()

        # Then update the labels:
        self.index.on_labels_update("l1", {"a": "a2", "b": "b1"})
        self.index.on_labels_update("l2", {"b": "b2"})
        self.assert_add("e1", "l1")
Ejemplo n.º 9
0
    def test_set_indexing(self):
        self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
        self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
        self.index.on_labels_update("l3", {"a": "a2", "b": "b3"})

        # Add an expression with existing labels.
        self.index.on_expression_update("e1",
                                        parse_selector("a in {'a', 'z'}"))
        self.assert_add("e1", "l1")
        self.assert_add("e1", "l2")

        # And one that uses a real set.
        self.index.on_expression_update("e2",
                                        parse_selector("b in {'b1', 'b2'}"))
        self.assert_add("e2", "l1")
        self.assert_add("e2", "l2")
        self.assert_no_updates()

        # Then update them.
        self.index.on_expression_update("e2",
                                        parse_selector("b in {'b2', 'b3'}"))
        self.assert_add("e2", "l3")
        self.assert_remove("e2", "l1")
        self.assert_no_updates()

        self.index.on_expression_update("e1", parse_selector("a in {'a2'}"))
        self.assert_add("e1", "l3")
        self.assert_remove("e1", "l1")
        self.assert_remove("e1", "l2")
        self.assert_no_updates()

        # Then update the labels:
        self.index.on_labels_update("l1", {"a": "a2", "b": "b1"})
        self.index.on_labels_update("l2", {"b": "b2"})
        self.assert_add("e1", "l1")
Ejemplo n.º 10
0
 def test_replace_selector_with_object(self):
     """
     Checks that the validate_profile() method replaces selectors
     (with their object representations).
     """
     policy = {
         "selector": "a == 'b'",
         "inbound_rules": [
             {"src_selector": "b == 'c'", "dst_selector": "e == 'f'"}
         ],
         "outbound_rules": [
             {"src_selector": "h == 'c'", "dst_selector": "i == 'f'"}
         ],
         "order": 10
     }
     common.validate_policy(TieredPolicyId("a", "b"), policy)
     self.assertEqual(policy["selector"], parse_selector("a == 'b'"))
     self.assertEqual(policy["inbound_rules"][0]["src_selector"],
                      parse_selector("b == 'c'"))
     self.assertEqual(policy["inbound_rules"][0]["dst_selector"],
                      parse_selector("e == 'f'"))
     self.assertEqual(policy["outbound_rules"][0]["src_selector"],
                      parse_selector("h == 'c'"))
     self.assertEqual(policy["outbound_rules"][0]["dst_selector"],
                      parse_selector("i == 'f'"))
Ejemplo n.º 11
0
    def test_selector(self):
        self.assert_rule_valid(
            {"src_selector": "foo == 'bar'"},
            {"src_selector": parse_selector("foo == 'bar'")})
        self.assert_rule_valid(
            {"dst_selector": "foo == 'bar'"},
            {"dst_selector": parse_selector("foo == 'bar'")})

        self.assert_rule_issue({"src_selector": "+"}, "Invalid !?src_selector")
        self.assert_rule_issue({"dst_selector": "+"}, "Invalid !?dst_selector")
Ejemplo n.º 12
0
 def test_non_equality_stale_match(self):
     self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
     self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
     self.index.on_labels_update("l3", {"a": "a", "b": "b3"})
     self.index.on_expression_update("e1", parse_selector('b == "b1" && ' 'a == "a"'))
     self.assert_add("e1", "l1")
     self.assert_no_updates()
     self.index.on_expression_update("e1", parse_selector('b == "b2" && ' 'a == "a"'))
     self.assert_remove("e1", "l1")
     self.assert_add("e1", "l2")
     self.assert_no_updates()
Ejemplo n.º 13
0
def check_no_match(selector, labels):
    expr = parse_selector(selector)
    assert_false(expr.evaluate(labels),
                 "%r unexpectedly matched %s" % (selector, labels))
    if selector.strip():
        # Check that wrapping the selector in a negation reverses its effect.
        negated_expr = parse_selector("!(%s)" % selector)
        assert_true(negated_expr.evaluate(labels),
                    "Negated version of %r unexpectedly matched %s" %
                    (selector, labels))
    assert_general_expression_properties(expr)
Ejemplo n.º 14
0
 def check_ids_equal(selector_1, selector_2):
     e = parse_selector(selector_1)
     uid_1 = e.unique_id
     e = parse_selector(selector_2)
     uid_2 = e.unique_id
     assert_equal(uid_1, uid_2)
     assert_true(re.match(r'^[a-zA-Z0-9_-]{38}$', uid_1))
     assert_not_in(uid_1, seen_ids,
                   msg="Selector %r unexpectedly had same unique ID as %r" %
                       (selector_1, seen_ids.get(uid_1)))
     seen_ids[uid_1] = selector_1
Ejemplo n.º 15
0
def check_no_match(selector, labels):
    expr = parse_selector(selector)
    assert_false(expr.evaluate(labels),
                 "%r unexpectedly matched %s" % (selector, labels))
    if selector.strip():
        # Check that wrapping the selector in a negation reverses its effect.
        negated_expr = parse_selector("!(%s)" % selector)
        assert_true(
            negated_expr.evaluate(labels),
            "Negated version of %r unexpectedly matched %s" %
            (selector, labels))
    assert_general_expression_properties(expr)
Ejemplo n.º 16
0
 def check_ids_equal(selector_1, selector_2):
     e = parse_selector(selector_1)
     uid_1 = e.unique_id
     e = parse_selector(selector_2)
     uid_2 = e.unique_id
     assert_equal(uid_1, uid_2)
     assert_true(re.match(r'^[a-zA-Z0-9_-]{38}$', uid_1))
     assert_not_in(uid_1,
                   seen_ids,
                   msg="Selector %r unexpectedly had same unique ID as %r" %
                   (selector_1, seen_ids.get(uid_1)))
     seen_ids[uid_1] = selector_1
Ejemplo n.º 17
0
def assert_general_expression_properties(expr):
    assert_equal(expr, expr, "%r wasn't equal to self" % expr)
    expr2 = parse_selector(str(expr))
    expr2a = parse_selector(str(expr))
    assert_true(expr2 is expr2a)
    expr3 = parse_selector(str(expr) + " ")
    assert_true(expr3 is not expr, "Expected to defeat cache")
    assert_equal(expr3, expr, "Selector parsed from equivalent input should "
                 "be equal")
    assert_false(expr3 != expr, "!= operator should be inverse of ==")
    assert_equal(hash(expr3), hash(expr),
                 "Hashes of equal objects should be equal")
    assert_equal(expr.unique_id, expr2.unique_id, "Unique ids should be equal")
    assert_equal(expr.unique_id, expr3.unique_id, "Unique ids should be equal")
Ejemplo n.º 18
0
    def test_selector(self):
        self.assert_rule_valid(
            {"src_selector": "foo == 'bar'"},
            {"src_selector": parse_selector("foo == 'bar'")}
        )
        self.assert_rule_valid(
            {"dst_selector": "foo == 'bar'"},
            {"dst_selector": parse_selector("foo == 'bar'")}
        )

        self.assert_rule_issue({"src_selector": "+"},
                               "Invalid !?src_selector")
        self.assert_rule_issue({"dst_selector": "+"},
                               "Invalid !?dst_selector")
Ejemplo n.º 19
0
def assert_general_expression_properties(expr):
    assert_equal(expr, expr, "%r wasn't equal to self" % expr)
    expr2 = parse_selector(str(expr))
    expr2a = parse_selector(str(expr))
    assert_true(expr2 is expr2a)
    expr3 = parse_selector(str(expr) + " ")
    assert_true(expr3 is not expr, "Expected to defeat cache")
    assert_equal(expr3, expr, "Selector parsed from equivalent input should "
                              "be equal")
    assert_false(expr3 != expr, "!= operator should be inverse of ==")
    assert_equal(hash(expr3), hash(expr),
                 "Hashes of equal objects should be equal")
    assert_equal(expr.unique_id, expr2.unique_id, "Unique ids should be equal")
    assert_equal(expr.unique_id, expr3.unique_id, "Unique ids should be equal")
Ejemplo n.º 20
0
 def test_non_equality_stale_match(self):
     self.index.on_labels_update("l1", {"a": "a", "b": "b1"})
     self.index.on_labels_update("l2", {"a": "a", "b": "b2"})
     self.index.on_labels_update("l3", {"a": "a", "b": "b3"})
     self.index.on_expression_update(
         "e1", parse_selector('b == "b1" && '
                              'a == "a"'))
     self.assert_add("e1", "l1")
     self.assert_no_updates()
     self.index.on_expression_update(
         "e1", parse_selector('b == "b2" && '
                              'a == "a"'))
     self.assert_remove("e1", "l1")
     self.assert_add("e1", "l2")
     self.assert_no_updates()
Ejemplo n.º 21
0
    def test_label_inheritance(self):
        # Make sure we have an endpoint so that we can check that it gets
        # put in the dirty set.  These have no labels at all so we test
        # that no labels gets translated to an empty dict.
        self.mgr.on_endpoint_update(ENDPOINT_ID, {"name": "tap12345",
                                                  "profile_ids": ["prof1"]},
                                    async=True)
        self.mgr.on_endpoint_update(ENDPOINT_ID_2, {"name": "tap23456",
                                                    "profile_ids": ["prof2"]},
                                    async=True)
        # And we need a selector to pick out one of the endpoints by the labels
        # attached to its parent.
        self.mgr.on_policy_selector_update(TieredPolicyId("a", "b"),
                                           parse_selector('a == "b"'),
                                           10,
                                           async=True)
        self.step_actor(self.mgr)

        with mock.patch.object(self.mgr, "_update_dirty_policy") as m_update:
            self.mgr.on_prof_labels_set("prof1", {"a": "b"}, async=True)
            self.step_actor(self.mgr)
            # Only the first endpoint should end up matching the selector.
            self.assertEqual(self.mgr.endpoints_with_dirty_policy,
                             set([ENDPOINT_ID]))
            # And an update should be triggered.
            self.assertEqual(m_update.mock_calls, [mock.call()])
Ejemplo n.º 22
0
    def test_endpoint_ip_update_with_selector_match(self):
        """
        Test a selector that relies on both directly-set labels and
        inherited ones.
        """
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("a == 'a1'")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1_LABELS, async=True)
        self.step_mgr()

        # Should now have a match.
        self.assert_one_selector_one_ep(selector)

        # Now update the IPs, should update the index.
        self.mgr.on_endpoint_update(EP_ID_1_1,
                                    EP_1_1_LABELS_NEW_IP,
                                    async=True)
        self.step_mgr()

        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag,
                         {selector: {
                             "10.0.0.2": ("dummy", EP_ID_1_1),
                         }})

        # Undo our messages to check that the index is correctly updated.
        self.mgr.decref(selector, async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, None, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 23
0
    def test_host_endpoint_expected_ips_indexed(self):
        """Check host endpoint expected IPs are added to index."""
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("all()")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_host_ep_update(HOST_EP_ID_1_1, HOST_EP_1_1, async=True)
        # Let the actor process them.
        self.step_mgr()

        # Check the indexes now contain the IP.
        self.assertEqual(self.mgr.endpoint_data_by_ep_id, {
            HOST_EP_ID_1_1: HOST_EP_DATA_1_1,
        })
        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag, {
            selector: {
                "10.0.0.1": ("dummy", HOST_EP_ID_1_1),
            }
        })

        # Undo our messages to check that the index is correctly updated.
        self.mgr.decref(selector, async=True)
        self.mgr.on_host_ep_update(HOST_EP_ID_1_1, None, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 24
0
    def test_non_trivial_selector_parent_match(self):
        """
        Test a selector that relies on both directly-set labels and
        inherited ones.
        """
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("a == 'a1' && p == 'p1'")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1_LABELS, async=True)
        # Let the actor process them.
        self.step_mgr()

        # Should be no match yet.
        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag, {})

        # Now fire in a parent label.
        self.mgr.on_prof_labels_set("prof1", {"p": "p1"}, async=True)
        self.step_mgr()

        # Should now have a match.
        self.assert_one_selector_one_ep(selector)

        # Undo our messages to check that the index is correctly updated.
        self.mgr.on_prof_labels_set("prof1", None, async=True)
        self.step_mgr()
        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag, {})
        self.mgr.decref(selector, async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, None, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 25
0
 def test_label_regex(self, label):
     """
     Test that the label validation logic matches the selector parsing
     logic in what it allows.
     """
     # Whitespace is ignored in selectors.
     assume(not re.search(r'\s', label))
     try:
         common.validate_labels("foo", {label: "foo"})
     except ValidationFailed:
         # Validation failed, should fail to parse as a selector too.
         _log.exception("Validation failed for label %r", label)
         assert_raises(BadSelector, parse_selector, "%s == 'a'" % label)
     else:
         # Validation passed, should be allowed in expression too.
         parse_selector("%s == 'a'" % label)
Ejemplo n.º 26
0
    def test_host_endpoint_expected_ips_indexed(self):
        """Check host endpoint expected IPs are added to index."""
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("all()")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_host_ep_update(HOST_EP_ID_1_1, HOST_EP_1_1, async=True)
        # Let the actor process them.
        self.step_mgr()

        # Check the indexes now contain the IP.
        self.assertEqual(self.mgr.endpoint_data_by_ep_id, {
            HOST_EP_ID_1_1: HOST_EP_DATA_1_1,
        })
        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag,
                         {selector: {
                             "10.0.0.1": ("dummy", HOST_EP_ID_1_1),
                         }})

        # Undo our messages to check that the index is correctly updated.
        self.mgr.decref(selector, async=True)
        self.mgr.on_host_ep_update(HOST_EP_ID_1_1, None, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 27
0
    def test_non_trivial_selector_parent_match(self):
        """
        Test a selector that relies on both directly-set labels and
        inherited ones.
        """
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("a == 'a1' && p == 'p1'")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1_LABELS, async=True)
        # Let the actor process them.
        self.step_mgr()

        # Should be no match yet.
        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag, {})

        # Now fire in a parent label.
        self.mgr.on_prof_labels_set("prof1", {"p": "p1"}, async=True)
        self.step_mgr()

        # Should now have a match.
        self.assert_one_selector_one_ep(selector)

        # Undo our messages to check that the index is correctly updated.
        self.mgr.on_prof_labels_set("prof1", None, async=True)
        self.step_mgr()
        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag, {})
        self.mgr.decref(selector, async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, None, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 28
0
    def test_endpoint_ip_update_with_selector_match(self):
        """
        Test a selector that relies on both directly-set labels and
        inherited ones.
        """
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("a == 'a1'")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1_LABELS, async=True)
        self.step_mgr()

        # Should now have a match.
        self.assert_one_selector_one_ep(selector)

        # Now update the IPs, should update the index.
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1_LABELS_NEW_IP,
                                    async=True)
        self.step_mgr()

        self.assertEqual(self.mgr.tag_membership_index.ip_owners_by_tag, {
            selector: {
                "10.0.0.2": ("dummy", EP_ID_1_1),
            }
        })

        # Undo our messages to check that the index is correctly updated.
        self.mgr.decref(selector, async=True)
        self.mgr.on_endpoint_update(EP_ID_1_1, None, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 29
0
 def test_label_regex(self, label):
     """
     Test that the label validation logic matches the selector parsing
     logic in what it allows.
     """
     # Whitespace is ignored in selectors.
     assume(not re.search(r'\s', label))
     try:
         common.validate_labels("foo", {label: "foo"})
     except ValidationFailed:
         # Validation failed, should fail to parse as a selector too.
         _log.exception("Validation failed for label %r", label)
         assert_raises(BadSelector, parse_selector,
                       "%s == 'a'" % label)
     else:
         # Validation passed, should be allowed in expression too.
         parse_selector("%s == 'a'" % label)
Ejemplo n.º 30
0
    def test_inheritance_index_mainline(self):
        ii = LabelInheritanceIndex(self.index)

        ii.on_item_update("item_1", {}, [])
        ii.on_item_update("item_2", {"a": "a1"}, [])
        ii.on_item_update("item_3", {}, ["parent_1"])
        ii.on_item_update("item_4", {"a": "a1"}, ["parent_2"])

        self.index.on_expression_update("e1", parse_selector("a == 'a1'"))
        self.index.on_expression_update("e2", parse_selector("a != 'a1'"))
        self.index.on_expression_update("e3", parse_selector("a == 'p1'"))

        self.assert_add("e1", "item_2")
        self.assert_add("e1", "item_4")
        self.assert_add("e2", "item_1")
        self.assert_add("e2", "item_3")
        self.assert_no_updates()

        # Now make a parent change, should cause a match.
        ii.on_parent_labels_update("parent_1", {"a": "p1"})
        self.assert_add("e3", "item_3")
        # Then, remove the parent label, should remove the match.
        ii.on_parent_labels_update("parent_1", {})
        self.assert_remove("e3", "item_3")

        # Now make a parent change, should cause a match.
        ii.on_parent_labels_update("parent_1", {"a": "p1"})
        self.assert_add("e3", "item_3")
        # Then, remove the parent labels entirely, should remove the match.
        ii.on_parent_labels_update("parent_1", None)
        self.assert_remove("e3", "item_3")

        # Now make a parent change for parent_2; the per-item labels should
        # override.
        ii.on_parent_labels_update("parent_2", {"a": "p1"})
        ii.on_parent_labels_update("parent_2", None)
        self.assert_no_updates()

        # Now make a parent change, should cause a match.
        ii.on_parent_labels_update("parent_1", {"a": "p1"})
        self.assert_add("e3", "item_3")
        # But then remove the item.
        ii.on_item_update("item_3", None, None)
        self.assert_remove("e3", "item_3")
        self.assert_remove("e2", "item_3")
Ejemplo n.º 31
0
    def test_inheritance_index_mainline(self):
        ii = LabelInheritanceIndex(self.index)

        ii.on_item_update("item_1", {}, [])
        ii.on_item_update("item_2", {"a": "a1"}, [])
        ii.on_item_update("item_3", {}, ["parent_1"])
        ii.on_item_update("item_4", {"a": "a1"}, ["parent_2"])

        self.index.on_expression_update("e1", parse_selector("a == 'a1'"))
        self.index.on_expression_update("e2", parse_selector("a != 'a1'"))
        self.index.on_expression_update("e3", parse_selector("a == 'p1'"))

        self.assert_add("e1", "item_2")
        self.assert_add("e1", "item_4")
        self.assert_add("e2", "item_1")
        self.assert_add("e2", "item_3")
        self.assert_no_updates()

        # Now make a parent change, should cause a match.
        ii.on_parent_labels_update("parent_1", {"a": "p1"})
        self.assert_add("e3", "item_3")
        # Then, remove the parent label, should remove the match.
        ii.on_parent_labels_update("parent_1", {})
        self.assert_remove("e3", "item_3")

        # Now make a parent change, should cause a match.
        ii.on_parent_labels_update("parent_1", {"a": "p1"})
        self.assert_add("e3", "item_3")
        # Then, remove the parent labels entirely, should remove the match.
        ii.on_parent_labels_update("parent_1", None)
        self.assert_remove("e3", "item_3")

        # Now make a parent change for parent_2; the per-item labels should
        # override.
        ii.on_parent_labels_update("parent_2", {"a": "p1"})
        ii.on_parent_labels_update("parent_2", None)
        self.assert_no_updates()

        # Now make a parent change, should cause a match.
        ii.on_parent_labels_update("parent_1", {"a": "p1"})
        self.assert_add("e3", "item_3")
        # But then remove the item.
        ii.on_item_update("item_3", None, None)
        self.assert_remove("e3", "item_3")
        self.assert_remove("e2", "item_3")
Ejemplo n.º 32
0
def validate_policy(policy_id, policy):
    """
    Validates and normalises the given policy dictionary.

    As side effects to the input dict:

    * Fields that are set to None in rules are removed completely.
    * Selectors are replaced with SelectorExpression objects.  Parsing now
      ensures that the selectors are valid and ensures that equal selectors
      compare and hash equally.  For example: "a == 'b'" and "a=='b'" are
      different strings that parse to the same selector.

    Once this routine has returned successfully, we know that all
    required fields are present and have valid values.

    :param TieredPolicyId policy_id: The ID of the profile, which is also
           validated.
    :param policy: The profile dict.  For example,
           {"inbound_rules": [...],
            "outbound_rules": [...],
            "selector": "foo == 'bar'",
            "order": 123}
    :raises ValidationFailed
    """
    # The code below relies on the top-level object to be a dict.  Check
    # that first.
    if not isinstance(policy, dict):
        raise ValidationFailed("Expected policy '%s' to be a dict, not %r." %
                               (policy_id, policy))

    issues = []

    for part in policy_id.tier, policy_id.policy_id:
        if not VALID_ID_RE.match(part):
            issues.append("Invalid profile ID '%r'." % policy_id)

    _validate_rules(policy, issues)

    if "selector" in policy:
        try:
            selector = parse_selector(policy["selector"])
        except BadSelector:
            issues.append("Failed to parse selector %s" % policy["selector"])
        else:
            policy["selector"] = selector
    else:
        issues.append("Profile missing required selector field")

    if "order" in policy:
        if not isinstance(policy["order"], numbers.Number):
            issues.append("Order should be a number, not %s" %
                          policy["order"])
    else:
        issues.append("Profile missing required order field")

    if issues:
        raise ValidationFailed(" ".join(issues))
Ejemplo n.º 33
0
def test_repr():
    sel = "a== 'a'  && c != 'd' ||  e in {'f'}  || d not in {'g' }"
    e = parse_selector(sel)
    assert_equal(repr(e),
                 "SelectorExpression<((a == 'a' && c != 'd') || "
                 "e in {'f'} || d not in {'g'})>")
    assert_equal(repr(e.expr_op),
                 "OrNode<((a == 'a' && c != 'd') || "
                 "e in {'f'} || d not in {'g'})>")
Ejemplo n.º 34
0
def test_plausible_garbage(l):
    try:
        sel = "".join(l)
        expr = parse_selector(sel)
    except BadSelector:
        pass
    else:
        assert isinstance(expr, SelectorExpression)
        expr.evaluate({})
Ejemplo n.º 35
0
def test_repr():
    sel = "a== 'a'  && c != 'd' ||  e in {'f'}  || d not in {'g' }"
    e = parse_selector(sel)
    assert_equal(
        repr(e), "SelectorExpression<((a == 'a' && c != 'd') || "
        "e in {'f'} || d not in {'g'})>")
    assert_equal(
        repr(e.expr_op), "OrNode<((a == 'a' && c != 'd') || "
        "e in {'f'} || d not in {'g'})>")
Ejemplo n.º 36
0
def test_plausible_garbage(l):
    try:
        sel = "".join(l)
        expr = parse_selector(sel)
    except BadSelector:
        pass
    else:
        assert isinstance(expr, SelectorExpression)
        expr.evaluate({})
Ejemplo n.º 37
0
def test_parse_gives_correct_exc_or_value(s):
    try:
        expr = parse_selector(s)
    except BadSelector:
        pass
    except KeyboardInterrupt:
        print "Interrupted while processing %r" % s
        raise
    else:
        assert isinstance(expr, SelectorExpression)
        expr.evaluate({})
Ejemplo n.º 38
0
def test_parse_gives_correct_exc_or_value(s):
    try:
        expr = parse_selector(s)
    except BadSelector:
        pass
    except KeyboardInterrupt:
        print "Interrupted while processing %r" % s
        raise
    else:
        assert isinstance(expr, SelectorExpression)
        expr.evaluate({})
Ejemplo n.º 39
0
    def test_host_endpoint_no_ips(self):
        """Check host endpoint expected IPs are added to index."""
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("all()")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_host_ep_update(HOST_EP_ID_1_1, HOST_EP_1_1_NO_IPS,
                                    async=True)
        # Let the actor process them.
        self.step_mgr()

        # Index should be empty because there's no contribution.
        self.assert_index_empty()
Ejemplo n.º 40
0
    def test_host_endpoint_no_ips(self):
        """Check host endpoint expected IPs are added to index."""
        # Send in the messages.  this selector should match even though there
        # are no labels in the endpoint.
        selector = parse_selector("all()")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        self.mgr.on_host_ep_update(HOST_EP_ID_1_1,
                                   HOST_EP_1_1_NO_IPS,
                                   async=True)
        # Let the actor process them.
        self.step_mgr()

        # Index should be empty because there's no contribution.
        self.assert_index_empty()
Ejemplo n.º 41
0
    def test_endpoint_then_selector(self):
        # Send in the messages.
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1, async=True)
        selector = parse_selector("all()")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        # Let the actor process them.
        self.step_mgr()

        self.assert_one_selector_one_ep(selector)

        # Undo our messages to check that the index is correctly updated.
        self.mgr.on_endpoint_update(EP_ID_1_1, None, async=True)
        self.mgr.decref(selector, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 42
0
    def test_endpoint_then_selector(self):
        # Send in the messages.
        self.mgr.on_endpoint_update(EP_ID_1_1, EP_1_1, async=True)
        selector = parse_selector("all()")
        self.mgr.get_and_incref(selector,
                                callback=self.on_ref_acquired,
                                async=True)
        # Let the actor process them.
        self.step_mgr()

        self.assert_one_selector_one_ep(selector)

        # Undo our messages to check that the index is correctly updated.
        self.mgr.on_endpoint_update(EP_ID_1_1, None, async=True)
        self.mgr.decref(selector, async=True)
        self.step_mgr()
        self.assert_index_empty()
Ejemplo n.º 43
0
def check_match(selector, labels):
    expr = parse_selector(selector)
    assert_true(expr.evaluate(labels),
                "%r did not match %s" % (selector, labels))
    assert_general_expression_properties(expr)
Ejemplo n.º 44
0
def check_no_match(selector, labels):
    expr = parse_selector(selector)
    assert_false(expr.evaluate(labels),
                 "%r unexpectedly matched %s" % (selector, labels))
    assert_general_expression_properties(expr)
Ejemplo n.º 45
0
def check_match(selector, labels):
    expr = parse_selector(selector)
    assert_true(expr.evaluate(labels),
                "%r did not match %s" % (selector, labels))
    assert_general_expression_properties(expr)
Ejemplo n.º 46
0
        '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP',
        '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT',
        '--append felix-INPUT --goto felix-FROM-HOST-IF ! --in-interface tap+',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 130',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 131',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 132',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 133',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 135',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 136',
        '--append felix-INPUT --protocol udp --sport 546 --dport 547 --jump ACCEPT',
        '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT',
        '--append felix-INPUT --jump felix-FROM-ENDPOINT',
    ]
}

SELECTOR_A_EQ_B = parse_selector("a == 'b'")

RULES_TESTS = [
    {
        "ip_version": 4,
        "tag_to_ipset": {},
        "sel_to_ipset": {
            SELECTOR_A_EQ_B: "a-eq-b"
        },
        "profile": {
            "id":
            "prof1",
            "inbound_rules": [{
                "src_selector": SELECTOR_A_EQ_B,
                "log_prefix": "foo",
                "action": "next-tier"
Ejemplo n.º 47
0
def check_no_match(selector, labels):
    expr = parse_selector(selector)
    assert_false(expr.evaluate(labels),
                 "%r unexpectedly matched %s" % (selector, labels))
    assert_general_expression_properties(expr)
Ejemplo n.º 48
0
def check_prereqs(selector, expected):
    expr = parse_selector(selector)
    assert_equal(expr.required_kvs, set(expected))
    assert_general_expression_properties(expr)
Ejemplo n.º 49
0
def _validate_rule_match_criteria(rule, issues, neg_pfx):
    """Validates and canonicalises a rule's match criteria.

    Each call validates either the negated or non-negated match criteria.
    I.e. the ones with "!" prefixed or not.

    :param rule: The dict for the individual rule.  If this contains selectors,
           they are replaced with parsed versions.  Protocols and IPs are
           also normalised.
    :param list[str] issues: List of issues found.  This method appends any
           issues it finds to the list.
    :param str neg_pfx: The negation prefix, "" for positive matches or "!"
           for negative.
    """
    # Absolutely all fields are optional, but some have valid and
    # invalid values.
    assert neg_pfx in ("", "!")

    # Explicitly get the non-negated protocol; even negated matches on port
    # or ICMP values require the protocol to be specified.
    protocol = rule.get("protocol")

    # Check the (possibly negated) profile.
    protocol_key = neg_pfx + 'protocol'
    maybe_neg_proto = rule.get(protocol_key)
    if maybe_neg_proto is not None and maybe_neg_proto not in KERNEL_PROTOCOLS:
        issues.append("Invalid %s %s in rule %s" %
                      (protocol_key, maybe_neg_proto, rule))
    elif maybe_neg_proto is not None:
        maybe_neg_proto = intern(str(maybe_neg_proto))
        rule[protocol_key] = str(maybe_neg_proto)

    ip_version = rule.get('ip_version')
    if ip_version == 4 and protocol == "icmpv6":
        issues.append("Using icmpv6 with IPv4 in rule %s." % rule)
    if ip_version == 6 and protocol == "icmp":
        issues.append("Using icmp with IPv6 in rule %s." % rule)

    for tag_type in (neg_pfx + 'src_tag', neg_pfx + 'dst_tag'):
        tag = rule.get(tag_type)
        if tag is None:
            continue
        if not VALID_ID_RE.match(tag):
            issues.append("Invalid %s: %r." % (tag_type, tag))

    # For selectors, we replace the value with the parsed selector.
    # This avoids having to re-parse it later and it ensures that
    # equivalent selectors compare equal.
    for sel_type in (neg_pfx + 'src_selector', neg_pfx + 'dst_selector'):
        sel_str = rule.get(sel_type)
        if sel_str is None:
            # sel_type wasn't present.
            continue
        try:
            sel = parse_selector(sel_str)
        except BadSelector:
            issues.append("Invalid %s: %r" % (sel_type, sel_str))
        else:
            rule[sel_type] = sel

    if "log_prefix" in rule:
        log_pfx = rule["log_prefix"]
        if not isinstance(log_pfx, basestring):
            issues.append("Log prefix should be a string")
        else:
            # Sanitize the log prefix.  iptables length limit is 29 chars but
            # we add ": " to the end in the iptables generator.
            rule["log_prefix"] = INVALID_LOG_KEY_CHARS.sub("_", log_pfx)[:27]

    for key in (neg_pfx + "src_net", neg_pfx + "dst_net"):
        network = rule.get(key)
        if (network is not None and
            not validate_cidr(rule[key], ip_version)):
            issues.append("Invalid CIDR (version %s) in rule %s." %
                          (ip_version, rule))
        elif network is not None:
            rule[key] = canonicalise_cidr(network, ip_version)
    for key in (neg_pfx + "src_ports", neg_pfx + "dst_ports"):
        ports = rule.get(key)
        if (ports is not None and
            not isinstance(ports, list)):
            issues.append("Expected ports to be a list in rule %s."
                          % rule)
            continue

        if ports is not None:
            if protocol not in KERNEL_PORT_PROTOCOLS:
                issues.append("%s is not allowed for protocol %s in "
                              "rule %s" % (key, protocol, rule))
            for port in ports:
                error = validate_rule_port(port)
                if error:
                    issues.append("Invalid port %s (%s) in rule %s." %
                                  (port, error, rule))

    action = rule.get(neg_pfx + 'action')
    if (action is not None and
            action not in KNOWN_ACTIONS):
        issues.append("Invalid action in rule %s." % rule)

    icmp_type = rule.get(neg_pfx + 'icmp_type')
    if icmp_type is not None:
        if not isinstance(icmp_type, int):
            issues.append("ICMP type is not an integer in rule %s." %
                          rule)
        elif not 0 <= icmp_type <= 255:
            issues.append("ICMP type is out of range in rule %s." %
                          rule)
    icmp_code = rule.get(neg_pfx + "icmp_code")
    if icmp_code is not None:
        if not isinstance(icmp_code, int):
            issues.append("ICMP code is not an integer in rule %s." %
                          rule)
        elif not 0 <= icmp_code <= 255:
            issues.append("ICMP code is out of range.")
        if icmp_type is None:
            # ICMP code without ICMP type not supported by iptables;
            # firewall against that.
            issues.append("ICMP code specified without ICMP type.")
Ejemplo n.º 50
0
def _validate_rule_match_criteria(rule, issues, neg_pfx):
    """Validates and canonicalises a rule's match criteria.

    Each call validates either the negated or non-negated match criteria.
    I.e. the ones with "!" prefixed or not.

    :param rule: The dict for the individual rule.  If this contains selectors,
           they are replaced with parsed versions.  Protocols and IPs are
           also normalised.
    :param list[str] issues: List of issues found.  This method appends any
           issues it finds to the list.
    :param str neg_pfx: The negation prefix, "" for positive matches or "!"
           for negative.
    """
    # Absolutely all fields are optional, but some have valid and
    # invalid values.
    assert neg_pfx in ("", "!")

    # Explicitly get the non-negated protocol; even negated matches on port
    # or ICMP values require the protocol to be specified.
    protocol = rule.get("protocol")

    # Check the (possibly negated) profile.
    protocol_key = neg_pfx + 'protocol'
    maybe_neg_proto = rule.get(protocol_key)
    if maybe_neg_proto is not None and maybe_neg_proto not in KERNEL_PROTOCOLS:
        issues.append("Invalid %s %s in rule %s" %
                      (protocol_key, maybe_neg_proto, rule))
    elif maybe_neg_proto is not None:
        maybe_neg_proto = intern(str(maybe_neg_proto))
        rule[protocol_key] = str(maybe_neg_proto)

    ip_version = rule.get('ip_version')
    if ip_version == 4 and protocol == "icmpv6":
        issues.append("Using icmpv6 with IPv4 in rule %s." % rule)
    if ip_version == 6 and protocol == "icmp":
        issues.append("Using icmp with IPv6 in rule %s." % rule)

    for tag_type in (neg_pfx + 'src_tag', neg_pfx + 'dst_tag'):
        tag = rule.get(tag_type)
        if tag is None:
            continue
        if not VALID_ID_RE.match(tag):
            issues.append("Invalid %s: %r." % (tag_type, tag))

    # For selectors, we replace the value with the parsed selector.
    # This avoids having to re-parse it later and it ensures that
    # equivalent selectors compare equal.
    for sel_type in (neg_pfx + 'src_selector', neg_pfx + 'dst_selector'):
        sel_str = rule.get(sel_type)
        if sel_str is None:
            # sel_type wasn't present.
            continue
        try:
            sel = parse_selector(sel_str)
        except BadSelector:
            issues.append("Invalid %s: %r" % (sel_type, sel_str))
        else:
            rule[sel_type] = sel

    for key in (neg_pfx + "src_net", neg_pfx + "dst_net"):
        network = rule.get(key)
        if (network is not None and not validate_cidr(rule[key], ip_version)):
            issues.append("Invalid CIDR (version %s) in rule %s." %
                          (ip_version, rule))
        elif network is not None:
            rule[key] = canonicalise_cidr(network, ip_version)
    for key in (neg_pfx + "src_ports", neg_pfx + "dst_ports"):
        ports = rule.get(key)
        if (ports is not None and not isinstance(ports, list)):
            issues.append("Expected ports to be a list in rule %s." % rule)
            continue

        if ports is not None:
            if protocol not in KERNEL_PORT_PROTOCOLS:
                issues.append("%s is not allowed for protocol %s in "
                              "rule %s" % (key, protocol, rule))
            for port in ports:
                error = validate_rule_port(port)
                if error:
                    issues.append("Invalid port %s (%s) in rule %s." %
                                  (port, error, rule))

    action = rule.get(neg_pfx + 'action')
    if (action is not None and action not in KNOWN_ACTIONS):
        issues.append("Invalid action in rule %s." % rule)

    icmp_type = rule.get(neg_pfx + 'icmp_type')
    if icmp_type is not None:
        if not isinstance(icmp_type, int):
            issues.append("ICMP type is not an integer in rule %s." % rule)
        elif not 0 <= icmp_type <= 255:
            issues.append("ICMP type is out of range in rule %s." % rule)
    icmp_code = rule.get(neg_pfx + "icmp_code")
    if icmp_code is not None:
        if not isinstance(icmp_code, int):
            issues.append("ICMP code is not an integer in rule %s." % rule)
        elif not 0 <= icmp_code <= 255:
            issues.append("ICMP code is out of range.")
        if icmp_type is None:
            # ICMP code without ICMP type not supported by iptables;
            # firewall against that.
            issues.append("ICMP code specified without ICMP type.")
Ejemplo n.º 51
0
def check_prereqs(selector, expected):
    expr = parse_selector(selector)
    assert_equal(expr.required_kvs, set(expected))
    assert_general_expression_properties(expr)
Ejemplo n.º 52
0
}
RULES_STR = json.dumps(RULES)

TAGS = ["a", "b"]
TAGS_STR = json.dumps(TAGS)

ETCD_ADDRESS = 'localhost:4001'

POLICY_ID = TieredPolicyId("tiername", "polname")
POLICY = {
    "selector": "a == 'b'",
    "inbound_rules": [],
    "outbound_rules": [],
    "order": 10,
}
SELECTOR = parse_selector(POLICY["selector"])
POLICY_PARSED = {
    "inbound_rules": [],
    "outbound_rules": [],
}
POLICY_STR = json.dumps(POLICY)


class TestEtcdAPI(BaseTestCase):
    def setUp(self):
        super(TestEtcdAPI, self).setUp()
        self.m_config = Mock(spec=Config)
        self.m_config.ETCD_ADDRS = [ETCD_ADDRESS]
        self.m_config.ETCD_SCHEME = "http"
        self.m_config.ETCD_KEY_FILE = None
        self.m_config.ETCD_CERT_FILE = None
Ejemplo n.º 53
0
        '--append felix-p-prof1-i --match set '
            '--match-set src-tag-name src '
            '--jump MARK --set-mark 0x1000000/0x1000000',
        '--append felix-p-prof1-i --match mark '
            '--mark 0x1000000/0x1000000 --jump RETURN',
    ],
    'felix-p-prof1-o': [
        '--append felix-p-prof1-o --match set '
            '--match-set dst-tag-name dst '
            '--jump MARK --set-mark 0x1000000/0x1000000',
        '--append felix-p-prof1-o --match mark '
            '--mark 0x1000000/0x1000000 --jump RETURN',
    ]
}

SELECTOR_1 = parse_selector("a == 'a1'")
RULES_2 = {
    "id": "prof1",
    "inbound_rules": [
        # Use negated matches to ensure we extract dependencies for negated
        # matches.
        {"!src_tag": "src-tag-added",
         "!src_selector": SELECTOR_1}
    ],
    "outbound_rules": [
        {"dst_tag": "dst-tag"}
    ]
}

RULES_2_CHAINS = {
    'felix-p-prof1-i': [
Ejemplo n.º 54
0
}
RULES_STR = json.dumps(RULES)

TAGS = ["a", "b"]
TAGS_STR = json.dumps(TAGS)

ETCD_ADDRESS = 'localhost:4001'

POLICY_ID = TieredPolicyId("tiername", "polname")
POLICY = {
    "selector": "a == 'b'",
    "inbound_rules": [],
    "outbound_rules": [],
    "order": 10,
}
SELECTOR = parse_selector(POLICY["selector"])
POLICY_PARSED = {
    "inbound_rules": [],
    "outbound_rules": [],
}
POLICY_STR = json.dumps(POLICY)

class TestEtcdAPI(BaseTestCase):
    def setUp(self):
        super(TestEtcdAPI, self).setUp()
        self.m_config = Mock(spec=Config)
        self.m_config.ETCD_ADDRS = [ETCD_ADDRESS]
        self.m_config.ETCD_SCHEME = "http"
        self.m_config.ETCD_KEY_FILE = None
        self.m_config.ETCD_CERT_FILE = None
        self.m_config.ETCD_CA_FILE = None
Ejemplo n.º 55
0
def _validate_rules(rules_dict, issues):
    """
    Validates and normalises the given rules dictionary.

    As side effects to the input dict:

    * Fields that are set to None in rules are removed completely.
    * Selectors are replaced with SelectorExpression objects.  Parsing now
      ensures that the selectors are valid and ensures that equal selectors
      compare and hash equally.  For example: "a == 'b'" and "a=='b'" are
      different strings that parse to the same selector.

    Once this routine has returned successfully, we know that all
    required fields are present and have valid values.

    :param dict rules_dict: profile dict as read from etcd
    :param list issues: Updated with any issues discovered.
    """
    for dirn in ("inbound_rules", "outbound_rules"):
        if dirn not in rules_dict:
            issues.append("No %s in rules." % dirn)
            continue

        if not isinstance(rules_dict[dirn], list):
            issues.append("Expected rules[%s] to be a list." % dirn)
            continue

        for rule in rules_dict[dirn]:
            if not isinstance(rule, dict):
                issues.append("Rule should be a dict: %r" % rule)
                break

            for key, value in rule.items():
                if value is None:
                    del rule[key]

            # Absolutely all fields are optional, but some have valid and
            # invalid values.
            protocol = rule.get('protocol')
            if protocol is not None and protocol not in KERNEL_PROTOCOLS:
                issues.append("Invalid protocol %s in rule %s" %
                              (protocol, rule))
            elif protocol is not None:
                protocol = intern(str(protocol))
                rule['protocol'] = str(protocol)

            ip_version = rule.get('ip_version')
            if ip_version is not None and ip_version not in (4, 6):
                # Bad IP version prevents further validation
                issues.append("Invalid ip_version in rule %s." % rule)
                continue

            if ip_version == 4 and protocol == "icmpv6":
                issues.append("Using icmpv6 with IPv4 in rule %s." % rule)
            if ip_version == 6 and protocol == "icmp":
                issues.append("Using icmp with IPv6 in rule %s." % rule)

            for tag_type in ('src_tag', 'dst_tag'):
                tag = rule.get(tag_type)
                if tag is None:
                    continue
                if not VALID_ID_RE.match(tag):
                    issues.append("Invalid %s: %r." % (tag_type, tag))

            # For selectors, we replace the value with the parsed selector.
            # This avoids having to re-parse it later and it ensures that
            # equivalent selectors compare equal.
            for sel_type in ('src_selector', 'dst_selector'):
                sel_str = rule.get(sel_type)
                if sel_str is None:
                    # sel_type wasn't present.
                    continue
                try:
                    sel = parse_selector(sel_str)
                except BadSelector:
                    issues.append("Invalid %s: %r" % (sel_type, sel_str))
                else:
                    rule[sel_type] = sel

            for key in ("src_net", "dst_net"):
                network = rule.get(key)
                if (network is not None and
                    not validate_cidr(rule[key], ip_version)):
                    issues.append("Invalid CIDR (version %s) in rule %s." %
                                  (ip_version, rule))
                elif network is not None:
                    rule[key] = canonicalise_cidr(network, ip_version)
            for key in ("src_ports", "dst_ports"):
                ports = rule.get(key)
                if (ports is not None and
                    not isinstance(ports, list)):
                    issues.append("Expected ports to be a list in rule %s."
                                  % rule)
                    continue

                if ports is not None:
                    if protocol not in KERNEL_PORT_PROTOCOLS:
                        issues.append("%s is not allowed for protocol %s in "
                                      "rule %s" % (key, protocol, rule))
                    for port in ports:
                        error = validate_rule_port(port)
                        if error:
                            issues.append("Invalid port %s (%s) in rule %s." %
                                          (port, error, rule))

            action = rule.get('action')
            if (action is not None and
                    action not in KNOWN_ACTIONS):
                issues.append("Invalid action in rule %s." % rule)

            icmp_type = rule.get('icmp_type')
            if icmp_type is not None:
                if not isinstance(icmp_type, int):
                    issues.append("ICMP type is not an integer in rule %s." %
                                  rule)
                elif not 0 <= icmp_type <= 255:
                    issues.append("ICMP type is out of range in rule %s." %
                                  rule)
            icmp_code = rule.get("icmp_code")
            if icmp_code is not None:
                if not isinstance(icmp_code, int):
                    issues.append("ICMP code is not an integer in rule %s." %
                                  rule)
                elif not 0 <= icmp_code <= 255:
                    issues.append("ICMP code is out of range.")
                if icmp_type is None:
                    # ICMP code without ICMP type not supported by iptables;
                    # firewall against that.
                    issues.append("ICMP code specified without ICMP type.")

            unknown_keys = set(rule.keys()) - KNOWN_RULE_KEYS
            if unknown_keys:
                issues.append("Rule contains unknown keys: %s." % unknown_keys)
Ejemplo n.º 56
0
        '--append felix-INPUT --match conntrack --ctstate INVALID --jump DROP',
        '--append felix-INPUT --match conntrack --ctstate RELATED,ESTABLISHED --jump ACCEPT',
        '--append felix-INPUT --goto felix-FROM-HOST-IF ! --in-interface tap+',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 130',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 131',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 132',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 133',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 135',
        '--append felix-INPUT --jump ACCEPT --protocol ipv6-icmp --icmpv6-type 136',
        '--append felix-INPUT --protocol udp --sport 546 --dport 547 --jump ACCEPT',
        '--append felix-INPUT --protocol udp --dport 53 --jump ACCEPT',
        '--append felix-INPUT --jump felix-FROM-ENDPOINT',
    ]
}

SELECTOR_A_EQ_B = parse_selector("a == 'b'")

RULES_TESTS = [
    {
        "ip_version": 4,
        "tag_to_ipset": {},
        "sel_to_ipset": {SELECTOR_A_EQ_B: "a-eq-b"},
        "profile": {
            "id": "prof1",
            "inbound_rules": [
                {"src_selector": SELECTOR_A_EQ_B,
                 "log_prefix": "foo",
                 "action": "next-tier"}
            ],
            "outbound_rules": [
                {"dst_selector": SELECTOR_A_EQ_B,