authz - bstoll/featureprofiles GitHub Wiki
Test gNSI API behaviors and gRPC authorization policy behaviors.
-
test_infra_id
: the SPIFFE-ID that is used by test infra clients.
Configure the DUT to enable the following services (that are using gRPC) are up, and use mTLS for authentication:
- gNMI
- gNOI
- gNSI
- gRIBI
NOTE: the support of SPIFFE-ID should NOT require explicitly pre-configured local users in the DUT config (for the purpose of 1:1 mapping of each SPIFFE-ID to a local user).
Prepare the following certs with the specified SPIFFE ID. Cert format details can be found in SPIFFE PR
-
cert_user_admin
withspiffe://test-abc.foo.bar/xyz/admin
-
cert_user_deny_all
withspiffe://test-abc.foo.bar/xyz/deny-all
-
cert_gribi_modify
withspiffe://test-abc.foo.bar/xyz/gribi-modify
-
cert_gnmi_set
withspiffe://test-abc.foo.bar/xyz/gnmi-set
-
cert_gnoi_time
withspiffe://test-abc.foo.bar/xyz/gnoi-time
-
cert_gnoi_ping
withspiffe://test-abc.foo.bar/xyz/gnoi-ping
-
cert_gnsi_probe
withspiffe://test-abc.foo.bar/xyz/gnsi-probe
-
cert_read_only
withspiffe://test-abc.foo.bar/xyz/read-only
NOTE: unless specifically mentioned, the rule allow-test-infra
MUST be attached to all the policies, so that the test or the test infra is not blocked from the device.
{
"name": "allow-test-infra",
"source": {
"principals": [
"<test_infra_id>"
]
},
"request": {}
},
Prepare the following gRPC authorization policies.
{
"name": "policy-everyone-can-gnmi-not-gribi",
"allow_rules": [
{
"name": "everyone-can-gnmi-get",
"source": {},
"request": {
"paths": [
"/gnmi.gNMI/Get"
]
}
}
],
"deny_rules": [
{
"name": "no-one-can-gribi-get",
"request": {
"paths": [
"/gribi.gRIBI/Get"
]
}
}
]
}
{
"name": "policy-everyone-can-gribi-not-gnmi",
"allow_rules": [
{
"name": "everyone-can-gribi",
"source": {
"principals": [
"*"
]
},
"request": {
"paths": [
"/gribi.gRIBI/*"
]
}
}
],
"deny_rules": [
{
"name": "no-one-can-gnmi",
"source": {
"principals": [
"*"
]
},
"request": {
"paths": [
"/gribi.gNMI/*"
]
}
}
]
}
{
"name": "policy-invalid-no-allow-rules",
"deny_rules": [
{
"name": "no-one-can-gribi",
"request": {
"paths": [
"/gribi.gRIBI/*"
]
}
}
]
}
{
"name": "policy-gribi-get",
"allow_rules": [
{
"name": "gribi-get",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/read-only"
]
},
"request": {
"paths": ["/gribi.gRIBI/Get"]
}
}
]
}
{
"name": "policy-gnmi-get",
"allow_rules": [
{
"name": "gnmi-get",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/read-only"
]
},
"request": {
"paths": ["/gnmi.gNMI/Get"]
}
}
]
}
The following table describes policy policy-normal-1
:
Cert | gRIBI.Modify | gRIBI.Get | gNMI.Set | gNMI.Get | gNOI.Time | gNOI.Ping | gNSI.Rotate | gNSI.Get | gNSI.Probe |
---|---|---|---|---|---|---|---|---|---|
cert_user_admin | allow | allow | allow | allow | allow | allow | allow | allow | allow |
cert_user_deny_all | deny | deny | deny | deny | deny | deny | deny | deny | deny |
cert_gribi_modify | allow | allow | deny | deny | deny | deny | deny | deny | deny |
cert_gnmi_set | deny | deny | allow | allow | deny | deny | deny | deny | deny |
cert_gnoi_time | deny | deny | deny | deny | allow | deny | deny | deny | deny |
cert_gnoi_ping | deny | deny | deny | deny | deny | allow | deny | deny | deny |
cert_gnsi_probe | deny | deny | deny | deny | deny | deny | deny | deny | allow |
cert_read_only | deny | allow | deny | allow | deny | deny | deny | allow | deny |
{
"name": "policy-normal-1",
"allow_rules": [
{
"name": "gribi-modify",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/admin",
"spiffe://test-abc.foo.bar/xyz/gribi-modify"
]
},
"request": {
"paths": ["/gribi.gRIBI/*"]
}
},
{
"name": "gnmi-set",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/admin",
"spiffe://test-abc.foo.bar/xyz/gnmi-set"
]
},
"request": {
"paths": ["/gnmi.gNMI/*"]
}
},
{
"name": "gnoi-time",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/admin",
"spiffe://test-abc.foo.bar/xyz/gnoi-time"
]
},
"request": {
"paths": ["/gnoi.system.System/Time"]
}
},
{
"name": "gnoi-ping",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/admin",
"spiffe://test-abc.foo.bar/xyz/gnoi-ping"
]
},
"request": {
"paths": ["/gnoi.system.System/Ping"]
}
},
{
"name": "gnsi-set",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/admin"
]
},
"request": {
"paths": ["/gnsi.authz.v1.Authz/*"]
}
},
{
"name": "gnsi-probe",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/gnsi-probe"
]
},
"request": {
"paths": ["/gnsi.authz.v1.Authz/Probe"]
}
},
{
"name": "read-only",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/read-only"
]
},
"request": {
"paths": [
"/gnmi.gNMI/Get",
"/gribi.gRIBI/Get",
"/gnsi.authz.v1.Authz/Get"
]
}
}
],
"deny_rules": [
{
"name": "deny-all-user-can-do-nothing",
"source": {
"principals": [
"spiffe://test-abc.foo.bar/xyz/deny_all"
]
},
"request": {
"paths": ["/*"]
}
}
]
}
NOTE: regarding gNMI OC validation:
- Everytime a gRPC call (including gNSI calls themselves) is allowed or denied, the following OC leaves should be validated:
-
/system/grpc-servers/grpc-server/authz-policy-counters/rpcs/rpc[name]/state/name
is the matched request path, e.g. "/gribi.gRIBI/Get" -
/system/grpc-servers/grpc-server/authz-policy-counters/rpcs/rpc/rpc[name]/state/access-accepts
increments if the rpc call is allowed. -
/system/grpc-servers/grpc-server/authz-policy-counters/rpcs/rpc/rpc[name]/state/access-rejects
increments if the rpc call is denied. -
/system/grpc-servers/grpc-server/authz-policy-counters/rpcs/rpc/rpc[name]/state/last-access-accept
reflects the timestamp of the method call. -
/system/grpc-servers/grpc-server/authz-policy-counters/rpcs/rpc/rpc[name]/state/last-access-reject
reflects the timestamp of the method call.
-
- Everytime a valid policy is pushed (even it's not finalized), the following OC leaves should be validated:
-
/system/grpc-servers/grpc-server/state/authz-policy-version
=UploadRequest.version
in the API proto. -
/system/grpc-servers/grpc-server/state/authz-policy-created-on
=UploadRequest.created_on
(in terms of represented time).
-
- Everytime a valid policy is automatically rolled back, the following OC leaves should be validated:
-
/system/grpc-servers/grpc-server/state/authz-policy-version
=UploadRequest.version
of the previous request (the one rollback to). -
/system/grpc-servers/grpc-server/state/authz-policy-created-on
=UploadRequest.created_on
of the previous request (the one rollback to).
-
- An invalid policy should not trigger the following OC leaf updates:
/system/grpc-servers/grpc-server/state/authz-policy-version
/system/grpc-servers/grpc-server/state/authz-policy-created-on
For each of the scenarios in this section, we need to exercise the following 3 actions to get the authorization results:
-
gNSI.Probe
afterUploadResponse
message but before theFinalizeRequest
message. -
gNSI.Probe
after theRotateAuthzRequest
call finished. -
The actual corresponding service client calls, after the
RotateAuthzRequest
call finished. -
Authz-1.1, "Test empty source"
- Use
gNSI.Rotate
method to push policypolicy-everyone-can-gnmi-not-gribi
, withcreate_on
=100
andversion
=policy-everyone-can-gnmi-not-gribi_v1
. - Ensure all results match per the following:
-
cert_user_admin
is allowed to issuegNMI.Get
method. -
cert_user_admin
is denied to issuegRIBI.Get
method.
-
- Use
-
Authz-1.2, "Test empty request"
- Use
gNSI.Rotate
method to push and finalize policypolicy-everyone-can-gribi-not-gnmi
, withcreate_on
=100
andversion
=policy-everyone-can-gribi-not-gnmi_v1
. - Ensure all results match per the following:
-
cert_user_deny_all
is denied to issuegNMI.Get
method. -
cert_user_admin
is allowed to issuegRIBI.Get
method.
-
- Use
-
Authz-1.3, "Test that there can only be one policy"
- Use
gNSI.Rotate
method to push and finalize policypolicy-gribi-get
, withcreate_on
=100
andversion
=policy-gribi-get_v1
. - Ensure all results match per the following:
-
cert_read_only
is allowed to issuegRIBI.Get
method. -
cert_read_only
is denied to issuegNMI.Get
method.
-
- Use
gNSI.Rotate
method to push and finalize policypolicy-gnmi-get
. - Ensure all results changed to the following:
-
cert_read_only
is denied to issuegRIBI.Get
method. -
cert_read_only
is allowed to issuegNMI.Get
method.
-
- Use
-
Authz-1.4, "Test normal policy"
- Use
gNSI.Rotate
method to push and finalize policypolicy-normal-1
, withcreate_on
=100
andversion
=policy-normal-1_v1
. - Ensure all results match per the above table for policy
policy-normal-1
.
- Use
-
TODO: Authz-1.5, "Test principle prefix and suffix match"
- Test the behavior of prefix and suffix match on principles
-
Authz-2.1, "Test only one rotation request at a time"
- Use
gNSI.Rotate
method to push policypolicy-everyone-can-gnmi-not-gribi
, but don't finalize it yet. - Initial another
gNSI.Rotate
method to push policypolicy-everyone-can-gribi-not-gnmi
, and expect to receive anUNAVAILABLE
gRPC error. - Ensure all actual client authorization result stays as per the following:
-
cert_user_admin
is allowed to issuegNMI.Get
method. -
cert_user_admin
is denied to issuegRIBI.Get
method.
-
- Use
-
Authz-2.2, "Test rollback when connection closed"
- Use
gNSI.Rotate
method to push and finalize policypolicy-gribi-get
. - Ensure
gNSI.Probe
result matches the following:-
cert_read_only
is allowed to issuegRIBI.Get
method. -
cert_read_only
is denied to issuegNMI.Get
method.
-
- Use
gNSI.Rotate
method to push policypolicy-gnmi-get
, but don't finalize it yet. - Ensure
gNSI.Probe
result matches the following:-
cert_read_only
is denied to issuegRIBI.Get
method. -
cert_read_only
is allowed to issuegNMI.Get
method.
-
- Close the gRPC session.
- Ensure
gNSI.Probe
result changed back to the following:-
cert_read_only
is allowed to issuegRIBI.Get
method. -
cert_read_only
is denied to issuegNMI.Get
method.
-
- Use
-
Authz-2.3, "Test rollback on invalid policy"
- Use
gNSI.Rotate
method to push and finalize policypolicy-gribi-get
. - Ensure
gNSI.Probe
result matches the following:-
cert_read_only
is allowed to issuegRIBI.Get
method. -
cert_read_only
is denied to issuegNMI.Get
method.
-
- Use
gNSI.Rotate
method to push policypolicy-invalid-no-allow-rules
, expect an error message and closed gRPC session. - Ensure
gNSI.Probe
result remains as the following:-
cert_read_only
is allowed to issuegRIBI.Get
method. -
cert_read_only
is denied to issuegNMI.Get
method.
-
- Use
-
Authz-2.4, "Test force_overwrite when the version does not change"
- Use
gNSI.Rotate
method to push and finalize policypolicy-gribi-get
. - Use
gNSI.Rotate
method to try to push policypolicy-gnmi-get
with version value not changed. Expect error message and closed gRPC session. - Validate that actual client authorization result stays as the following:
-
cert_read_only
is allowed to issuegRIBI.Get
method. -
cert_read_only
is denied to issuegNMI.Get
method.
-
- Use
gNSI.Rotate
method to try to push policypolicy-gnmi-get
with version value, butforce_overwrite
set to true. Expect no error message, and the push can be finalized. - Ensure actual client authorization results are changed to the following:
-
cert_read_only
is denied to issuegRIBI.Get
method. -
cert_read_only
is allowed to issuegNMI.Get
method.
-
- Use
- Use
gNSI.Rotate
method to push and finalize policypolicy-gribi-get
. - Wait for 30s, intial
gNSI.Get
and validate the value ofversion
,created_on
and gRPC policy content does not change.
- Use
gNSI.Rotate
method to push and finalize policypolicy-normal-1
. - Reboot the device.
- Reconnect to the device, issue
gNSI.Get
andgNMI.Get
and validate the value ofversion
,created_on
and gRPC policy content does not change. - Ensure actual corresponding clients are authorized per the the above table for policy
policy-normal-1
.
The below yaml defines the OC paths intended to be covered by this test. OC paths used for test setup are not listed here.
TODO(OCRPC): Record is not complete
rpcs:
gnsi:
authz.v1.Authz.Get: