-
Notifications
You must be signed in to change notification settings - Fork 20
Add tests for mTLS configuration and functional behavior #675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WIP Review
20a477f
to
c3dbc9d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test_mtls_configuration_with_*
could be put in one file with some parametrization
same with test_mtls_functional_behavior_*
https://docs.pytest.org/en/7.1.x/example/parametrize.html
Or look how its done for example here:
testsuite/tests/kuadrantctl/cli/test_simple_route.py
or
testsuite/tests/singlecluster/authorino/operator/clusterwide/test_clusterwide.py
testsuite/tests/singlecluster/mtls/test_mtls_configuration_with_authpolicy.py
Outdated
Show resolved
Hide resolved
testsuite/tests/singlecluster/mtls/test_mtls_configuration_with_authpolicy.py
Outdated
Show resolved
Hide resolved
testsuite/tests/singlecluster/mtls/test_mtls_functional_behavior_with_authpolicy.py
Outdated
Show resolved
Hide resolved
testsuite/tests/singlecluster/mtls/test_mtls_functional_behavior_with_authpolicy.py
Outdated
Show resolved
Hide resolved
c3dbc9d
to
fc1902c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job with the parametrization!
|
||
pytestmark = [pytest.mark.kuadrant_only, pytest.mark.disruptive] | ||
|
||
component_cases = ["limitador", "authorino", "both"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if instead of defining a new symbol "both" you would use lists like this:
component_cases = ["limitador", "authorino", "both"] | |
component_cases = [["limitador"], ["authorino"], ["limitador", "authorino"]] |
That could simplify later logic.
if component == "limitador": | ||
rate_limit.wait_for_ready() | ||
|
||
responses = client.get_many("/get", 2) | ||
responses.assert_all(status_code=200) | ||
assert client.get("/get").status_code == 429 | ||
|
||
elif component == "authorino": | ||
authorization.wait_for_ready() | ||
response = client.get("/get", auth=auth) | ||
|
||
assert response.status_code == 200 | ||
|
||
response = client.get("/get") | ||
assert response.status_code == 401 | ||
|
||
elif component == "both": | ||
authorization.wait_for_ready() | ||
rate_limit.wait_for_ready() | ||
|
||
response = client.get("/get", auth=auth) | ||
assert response.status_code == 200 | ||
|
||
response = client.get("/get") | ||
assert response.status_code == 401 | ||
|
||
response = client.get("/get", auth=auth) | ||
assert response.status_code == 429 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example this could change to
if limitador in component:
...
if authorino in component:
...
To prevent authorino testing with "auth" interfering with limitador testing with "get_many" you can create "when" rule for different paths. For example have path /anything/limitador
being ratelimited and /anything/authorino
requiring authentication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I implemented all the changes you suggested in relation to this, but I have still had to add "auth" to the limitador requests when authorino is also enabled because even with a when
condition, the AuthPolicy
was still causing 401s on /anything/limitador
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, maybe its due to the rule that if at least one identity is defined than at least one must match otherwise the default response is 401. Maybe adding anonymous auth for the /anything/limitador
could solve this 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will give this a try!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That has worked, thanks 😄
def rate_limit(cluster, blame, module_label, gateway, route): # pylint: disable=unused-argument | ||
"""RateLimitPolicy for testing""" | ||
policy = RateLimitPolicy.create_instance(cluster, blame("limit"), gateway, labels={"testRun": module_label}) | ||
policy.add_limit("basic", [Limit(2, "10s")]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example you can add when rule like this.
policy.add_limit("basic", [Limit(2, "10s")]) | |
policy.add_limit("basic", [Limit(2, "10s")], when=[CelPredicate("request.path == '/anything/limitador'")]) |
@pytest.fixture(scope="module") | ||
def authorization(component, request): | ||
"""Enable AuthPolicy when component is 'authorino' or 'both'""" | ||
if component in ("authorino", "both"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And for authorization having bit trickier way but still possible:
authorization.identity.clear_all()
authorization.identity.add_oidc("default", oidc_provider.well_known["issuer"], when=[CelPredicate("request.path == '/anything/authorino'"))
request.addfinalizer(_reset) | ||
|
||
|
||
def get_components_to_check(component): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then you would not need this helper function
"""Tests that requests succeed when mTLS is disabled""" | ||
configure_mtls(False) | ||
|
||
assert kuadrant.model.spec.mtls.enable is False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is
operator is usually used for checking if the variable is None
(due to Python considering certain non-bool values as bool ones, like empty [] list and so on) not really for normal equality checking, thats what ==
is for.
Yes is
operator can also be used if you really want to know two variables point to the same object and are not just equal, but not sure if we use such equality checking in our testsuite.
In this case it will still work as these primitive type objects in python have all same id
value - which is what is
operator uses for comparison.
|
||
|
||
@pytest.mark.parametrize("component", component_cases, indirect=True) | ||
def test_requests_still_succeed_after_mtls_disabled_again( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test case depends on the test order. Yes normally all test functions run one after other in a module file but this is not guaranteed. What if you run this test on its own? Generally you would want to avoid test cases which depend on other test cases as well. So what I would maybe do is add this to the beginning of the test to ensure the mtls was on before:
configure_mtls(True)
...wait for status here...
Adn than continue with disabling mtls and checking
peer_auths = selector("peerauthentication").objects() | ||
assert peer_auths, "No PeerAuthentication resources found" | ||
strict = [pa.name() for pa in peer_auths if pa.model.spec.mtls.mode == "STRICT"] | ||
assert strict, "No PeerAuthentication with mtls.mode == 'STRICT'" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add one more assert checking that:
spec.selector.matchLabels == {kuadrant.io/managed: 'true'}
wait_for_status(kuadrant, expected=True, component=comp) | ||
|
||
for comp in components_to_check: | ||
pod = wait_for_injected_pod(comp) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do a assert if the pod is None which means the pod with sidecar was not created
Signed-off-by: emmaaroche <[email protected]>
fc1902c
to
98c716e
Compare
Description:
This PR introduces tests that validate Kuadrant's mTLS functionality.
The tests are grouped into configuration and functional behavior checks, and are applied across three scenarios:
Each scenario includes the following tests:
mTLS Functional Behavior Tests:
mTLS Configuration Tests:
PeerAuthentication
withSTRICT
mode is created when mTLS is enabledReady
status after enabling mTLSRelated Issues / PR's:
#1230
#1329
#1170