authz_test.py
Go to the documentation of this file.
1 # Copyright 2021 gRPC authors.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import datetime
16 import time
17 from typing import Optional
18 
19 from absl import flags
20 from absl.testing import absltest
21 import grpc
22 
23 from framework import xds_k8s_testcase
24 from framework.helpers import skips
25 
26 flags.adopt_module_key_flags(xds_k8s_testcase)
27 
28 # Type aliases
29 _XdsTestServer = xds_k8s_testcase.XdsTestServer
30 _XdsTestClient = xds_k8s_testcase.XdsTestClient
31 _SecurityMode = xds_k8s_testcase.SecurityXdsKubernetesTestCase.SecurityMode
32 _Lang = skips.Lang
33 
34 # The client generates QPS even when it is still loading information from xDS.
35 # Once it finally connects there will be an outpouring of the bufferred RPCs and
36 # the server needs time to chew through the backlog, especially since it is
37 # still a new process and so probably interpreted. The server on one run
38 # processed 225 RPCs a second, so with the client configured for 25 qps this is
39 # 40 seconds worth of buffering before starting to drain the backlog.
40 _SETTLE_DURATION = datetime.timedelta(seconds=5)
41 _SAMPLE_DURATION = datetime.timedelta(seconds=0.5)
42 
43 
44 class AuthzTest(xds_k8s_testcase.SecurityXdsKubernetesTestCase):
45  RPC_TYPE_CYCLE = {
46  'UNARY_CALL': 'EMPTY_CALL',
47  'EMPTY_CALL': 'UNARY_CALL',
48  }
49 
50  @staticmethod
51  def is_supported(config: skips.TestConfig) -> bool:
52  # Per "Authorization (RBAC)" in
53  # https://github.com/grpc/grpc/blob/master/doc/grpc_xds_features.md
54  if config.client_lang in _Lang.CPP | _Lang.PYTHON:
55  return config.version_gte('v1.44.x')
56  elif config.client_lang in _Lang.GO | _Lang.JAVA:
57  return config.version_gte('v1.42.x')
58  return True
59 
60  def setUp(self):
61  super().setUp()
62  self.next_rpc_type: Optional[int] = None
63 
64  def authz_rules(self):
65  return [
66  {
67  "destinations": {
68  "hosts": [f"*:{self.server_xds_port}"],
69  "ports": [self.server_port],
70  "httpHeaderMatch": {
71  "headerName": "test",
72  "regexMatch": "host-wildcard",
73  },
74  },
75  },
76  {
77  "destinations": {
78  "hosts": [f"*:{self.server_xds_port}"],
79  "ports": [self.server_port],
80  "httpHeaderMatch": {
81  "headerName": "test",
82  "regexMatch": "header-regex-a+",
83  },
84  },
85  },
86  {
87  "destinations": [{
88  "hosts": [f"{self.server_xds_host}:{self.server_xds_port}"],
89  "ports": [self.server_port],
90  "httpHeaderMatch": {
91  "headerName": "test",
92  "regexMatch": "host-match1",
93  },
94  }, {
95  "hosts": [
96  f"a-not-it.com:{self.server_xds_port}",
97  f"{self.server_xds_host}:{self.server_xds_port}",
98  "z-not-it.com:1",
99  ],
100  "ports": [1, self.server_port, 65535],
101  "httpHeaderMatch": {
102  "headerName": "test",
103  "regexMatch": "host-match2",
104  },
105  }],
106  },
107  {
108  "destinations": {
109  "hosts": [
110  f"not-the-host:{self.server_xds_port}",
111  "not-the-host",
112  ],
113  "ports": [self.server_port],
114  "httpHeaderMatch": {
115  "headerName": "test",
116  "regexMatch": "never-match-host",
117  },
118  },
119  },
120  {
121  "destinations": {
122  "hosts": [f"*:{self.server_xds_port}"],
123  "ports": [1],
124  "httpHeaderMatch": {
125  "headerName": "test",
126  "regexMatch": "never-match-port",
127  },
128  },
129  },
130  # b/202058316. The wildcard principal is generating invalid config
131  # {
132  # "sources": {
133  # "principals": ["*"],
134  # },
135  # "destinations": {
136  # "hosts": [f"*:{self.server_xds_port}"],
137  # "ports": [self.server_port],
138  # "httpHeaderMatch": {
139  # "headerName": "test",
140  # "regexMatch": "principal-present",
141  # },
142  # },
143  # },
144  {
145  "sources": [{
146  "principals": [
147  f"spiffe://{self.project}.svc.id.goog/not/the/client",
148  ],
149  }, {
150  "principals": [
151  f"spiffe://{self.project}.svc.id.goog/not/the/client",
152  f"spiffe://{self.project}.svc.id.goog/ns/"
153  f"{self.client_namespace}/sa/{self.client_name}",
154  ],
155  }],
156  "destinations": {
157  "hosts": [f"*:{self.server_xds_port}"],
158  "ports": [self.server_port],
159  "httpHeaderMatch": {
160  "headerName": "test",
161  "regexMatch": "match-principal",
162  },
163  },
164  },
165  {
166  "sources": {
167  "principals": [
168  f"spiffe://{self.project}.svc.id.goog/not/the/client",
169  ],
170  },
171  "destinations": {
172  "hosts": [f"*:{self.server_xds_port}"],
173  "ports": [self.server_port],
174  "httpHeaderMatch": {
175  "headerName": "test",
176  "regexMatch": "never-match-principal",
177  },
178  },
179  },
180  ]
181 
182  def configure_and_assert(self, test_client: _XdsTestClient,
183  test_metadata_val: Optional[str],
184  status_code: grpc.StatusCode) -> None:
185  # Swap method type every sub-test to avoid mixing results
186  rpc_type = self.next_rpc_type
187  if rpc_type is None:
188  stats = test_client.get_load_balancer_accumulated_stats()
189  for t in self.RPC_TYPE_CYCLE:
190  if not stats.stats_per_method[t].rpcs_started:
191  rpc_type = t
192  self.assertIsNotNone(rpc_type, "All RPC types already used")
193  self.next_rpc_type = self.RPC_TYPE_CYCLE[rpc_type]
194 
195  metadata = None
196  if test_metadata_val is not None:
197  metadata = ((rpc_type, "test", test_metadata_val),)
198  test_client.update_config.configure(rpc_types=[rpc_type],
199  metadata=metadata)
200  self.assertRpcStatusCodes(test_client,
201  status_code=status_code,
202  duration=_SAMPLE_DURATION,
203  method=rpc_type)
204 
205  def test_plaintext_allow(self) -> None:
206  self.setupTrafficDirectorGrpc()
207  self.td.create_authz_policy(action='ALLOW', rules=self.authz_rules())
208  self.setupSecurityPolicies(server_tls=False,
209  server_mtls=False,
210  client_tls=False,
211  client_mtls=False)
212 
213  test_server: _XdsTestServer = self.startSecureTestServer()
214  self.setupServerBackends()
215  test_client: _XdsTestClient = self.startSecureTestClient(test_server)
216  time.sleep(_SETTLE_DURATION.total_seconds())
217 
218  with self.subTest('01_host_wildcard'):
219  self.configure_and_assert(test_client, 'host-wildcard',
220  grpc.StatusCode.OK)
221 
222  with self.subTest('02_no_match'):
223  self.configure_and_assert(test_client, 'no-such-rule',
224  grpc.StatusCode.PERMISSION_DENIED)
225  self.configure_and_assert(test_client, None,
226  grpc.StatusCode.PERMISSION_DENIED)
227 
228  with self.subTest('03_header_regex'):
229  self.configure_and_assert(test_client, 'header-regex-a',
230  grpc.StatusCode.OK)
231  self.configure_and_assert(test_client, 'header-regex-aa',
232  grpc.StatusCode.OK)
233  self.configure_and_assert(test_client, 'header-regex-',
234  grpc.StatusCode.PERMISSION_DENIED)
235  self.configure_and_assert(test_client, 'header-regex-ab',
236  grpc.StatusCode.PERMISSION_DENIED)
237  self.configure_and_assert(test_client, 'aheader-regex-a',
238  grpc.StatusCode.PERMISSION_DENIED)
239 
240  with self.subTest('04_host_match'):
241  self.configure_and_assert(test_client, 'host-match1',
242  grpc.StatusCode.OK)
243  self.configure_and_assert(test_client, 'host-match2',
244  grpc.StatusCode.OK)
245 
246  with self.subTest('05_never_match_host'):
247  self.configure_and_assert(test_client, 'never-match-host',
248  grpc.StatusCode.PERMISSION_DENIED)
249 
250  with self.subTest('06_never_match_port'):
251  self.configure_and_assert(test_client, 'never-match-port',
252  grpc.StatusCode.PERMISSION_DENIED)
253 
254  # b/202058316
255  # with self.subTest('07_principal_present'):
256  # self.configure_and_assert(test_client, 'principal-present',
257  # grpc.StatusCode.PERMISSION_DENIED)
258 
259  def test_tls_allow(self) -> None:
260  self.setupTrafficDirectorGrpc()
261  self.td.create_authz_policy(action='ALLOW', rules=self.authz_rules())
262  self.setupSecurityPolicies(server_tls=True,
263  server_mtls=False,
264  client_tls=True,
265  client_mtls=False)
266 
267  test_server: _XdsTestServer = self.startSecureTestServer()
268  self.setupServerBackends()
269  test_client: _XdsTestClient = self.startSecureTestClient(test_server)
270  time.sleep(_SETTLE_DURATION.total_seconds())
271 
272  with self.subTest('01_host_wildcard'):
273  self.configure_and_assert(test_client, 'host-wildcard',
274  grpc.StatusCode.OK)
275 
276  with self.subTest('02_no_match'):
277  self.configure_and_assert(test_client, None,
278  grpc.StatusCode.PERMISSION_DENIED)
279 
280  # b/202058316
281  # with self.subTest('03_principal_present'):
282  # self.configure_and_assert(test_client, 'principal-present',
283  # grpc.StatusCode.PERMISSION_DENIED)
284 
285  def test_mtls_allow(self) -> None:
286  self.setupTrafficDirectorGrpc()
287  self.td.create_authz_policy(action='ALLOW', rules=self.authz_rules())
288  self.setupSecurityPolicies(server_tls=True,
289  server_mtls=True,
290  client_tls=True,
291  client_mtls=True)
292 
293  test_server: _XdsTestServer = self.startSecureTestServer()
294  self.setupServerBackends()
295  test_client: _XdsTestClient = self.startSecureTestClient(test_server)
296  time.sleep(_SETTLE_DURATION.total_seconds())
297 
298  with self.subTest('01_host_wildcard'):
299  self.configure_and_assert(test_client, 'host-wildcard',
300  grpc.StatusCode.OK)
301 
302  with self.subTest('02_no_match'):
303  self.configure_and_assert(test_client, None,
304  grpc.StatusCode.PERMISSION_DENIED)
305 
306  # b/202058316
307  # with self.subTest('03_principal_present'):
308  # self.configure_and_assert(test_client, 'principal-present',
309  # grpc.StatusCode.OK)
310 
311  with self.subTest('04_match_principal'):
312  self.configure_and_assert(test_client, 'match-principal',
313  grpc.StatusCode.OK)
314 
315  with self.subTest('05_never_match_principal'):
316  self.configure_and_assert(test_client, 'never-match-principal',
317  grpc.StatusCode.PERMISSION_DENIED)
318 
319  def test_plaintext_deny(self) -> None:
320  self.setupTrafficDirectorGrpc()
321  self.td.create_authz_policy(action='DENY', rules=self.authz_rules())
322  self.setupSecurityPolicies(server_tls=False,
323  server_mtls=False,
324  client_tls=False,
325  client_mtls=False)
326 
327  test_server: _XdsTestServer = self.startSecureTestServer()
328  self.setupServerBackends()
329  test_client: _XdsTestClient = self.startSecureTestClient(test_server)
330  time.sleep(_SETTLE_DURATION.total_seconds())
331 
332  with self.subTest('01_host_wildcard'):
333  self.configure_and_assert(test_client, 'host-wildcard',
334  grpc.StatusCode.PERMISSION_DENIED)
335 
336  with self.subTest('02_no_match'):
337  self.configure_and_assert(test_client, None, grpc.StatusCode.OK)
338 
339 
340 if __name__ == '__main__':
341  absltest.main()
tests.authz_test.AuthzTest.setUp
def setUp(self)
Definition: authz_test.py:60
tests.authz_test.AuthzTest.configure_and_assert
None configure_and_assert(self, _XdsTestClient test_client, Optional[str] test_metadata_val, grpc.StatusCode status_code)
Definition: authz_test.py:182
tests.authz_test.AuthzTest.test_plaintext_deny
None test_plaintext_deny(self)
Definition: authz_test.py:319
framework.helpers
Definition: tools/run_tests/xds_k8s_test_driver/framework/helpers/__init__.py:1
tests.authz_test.AuthzTest.next_rpc_type
next_rpc_type
Definition: authz_test.py:191
grpc.StatusCode
Definition: src/python/grpcio/grpc/__init__.py:232
tests.authz_test.AuthzTest.test_plaintext_allow
None test_plaintext_allow(self)
Definition: authz_test.py:205
tests.authz_test.AuthzTest.is_supported
bool is_supported(skips.TestConfig config)
Definition: authz_test.py:51
tests.authz_test.AuthzTest.RPC_TYPE_CYCLE
dictionary RPC_TYPE_CYCLE
Definition: authz_test.py:45
tests.authz_test.AuthzTest
Definition: authz_test.py:44
tests.authz_test.AuthzTest.authz_rules
def authz_rules(self)
Definition: authz_test.py:64
tests.authz_test.AuthzTest.test_mtls_allow
None test_mtls_allow(self)
Definition: authz_test.py:285
tests.authz_test.AuthzTest.test_tls_allow
None test_tls_allow(self)
Definition: authz_test.py:259


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:35