diff --git a/feature/gnsi/pathz/tests/pathz/README.md b/feature/gnsi/pathz/tests/pathz/README.md new file mode 100644 index 00000000000..0563f56229e --- /dev/null +++ b/feature/gnsi/pathz/tests/pathz/README.md @@ -0,0 +1,208 @@ +# Pathz: Path-level Authorization (1-3) tests + +## Summary + +Test gNSI Pathz API behaviors and path-level authorization enforcement. + +## Testbed type + +* [`featureprofiles/topologies/dut.testbed`](https://github.com/openconfig/featureprofiles/blob/main/topologies/dut.testbed) + +## Procedure + +### Baseline Setup + +#### DUT service setup + +Configure the DUT to enable the following services (that are using gRPC) and +use mTLS for authentication: + +* gNMI +* gNSI + +The support of SPIFFE-ID should NOT require explicitly pre-configured local +users in the DUT config. + +#### Client certs + +Prepare the following client certs with the specified SPIFFE ID: + +* `gnmi_admin` with `spiffe://test-realm.foo.bar/role/admin` +* `gnmi_reader` with `spiffe://test-realm.foo.bar/role/reader` +* `gnmi_unauthorized` with `spiffe://test-realm.foo.bar/role/unauthorized` + +#### Pathz Authorization Policy + +The policy used for enforcement tests is defined below. + +```json +{ + "rules": [ + { + "id": "allow-reader-read-system", + "user": "spiffe://test-realm.foo.bar/role/reader", + "path": { + "elem": [ + {"name": "system"} + ] + }, + "action": "ACTION_PERMIT", + "mode": "MODE_READ" + }, + { + "id": "deny-reader-write-system", + "user": "spiffe://test-realm.foo.bar/role/reader", + "path": { + "elem": [ + {"name": "system"} + ] + }, + "action": "ACTION_DENY", + "mode": "MODE_WRITE" + }, + { + "id": "allow-admin-write-interfaces", + "group": "admin-group", + "path": { + "elem": [ + {"name": "interfaces"}, + {"name": "interface"} + ] + }, + "action": "ACTION_PERMIT", + "mode": "MODE_WRITE" + }, + { + "id": "deny-admin-write-port1", + "user": "spiffe://test-realm.foo.bar/role/admin", + "path": { + "elem": [ + {"name": "interfaces"}, + {"name": "interface", "key": {"name": ""}} + ] + }, + "action": "ACTION_DENY", + "mode": "MODE_WRITE" + } + ], + "groups": [ + { + "name": "admin-group", + "users": [ + {"name": "spiffe://test-realm.foo.bar/role/admin"} + ] + } + ] +} +``` + +### Pathz-1: Policy Rotation and Freshness Verification + +This test verifies the rotation mechanism of the Pathz policy and the +correctness of the telemetry reporting the policy status. + +#### Procedure: + +1. **Initial Push**: + * Use `gNSI.Pathz.Rotate` to push the baseline Pathz policy. + * Set `version` to `v1` and `created_on` to a valid timestamp (e.g., `100`). + * Do **not** finalize yet. +2. **Telemetry Verification (Sandbox)**: + * Verify that the sandbox policy version and creation time are updated in telemetry: + * `/system/gnmi-pathz-policies/policies/policy[instance=SANDBOX]/state/version` should be `v1`. + * `/system/gnmi-pathz-policies/policies/policy[instance=SANDBOX]/state/created-on` should match the pushed timestamp. +3. **Rollback on Disconnect**: + * Close the gRPC session without sending `Finalize`. + * Verify that the sandbox policy is cleared or rolled back. +4. **Finalize Rotation**: + * Start a new rotation, push the policy (`v1`), and send `Finalize`. + * Verify that the active policy is now updated: + * `/system/gnmi-pathz-policies/policies/policy[instance=ACTIVE]/state/version` should be `v1`. + * `/system/gnmi-pathz-policies/policies/policy[instance=ACTIVE]/state/created-on` should match the timestamp. +5. **Get Verification**: + * Call `gNSI.Pathz.Get` and verify that the returned policy matches the pushed `v1` policy. +6. **Force Overwrite**: + * Attempt to push the same policy version `v1` without changing the version string. The rotation should fail with an error. + * Push the policy again with the same version string `v1` but set `force_overwrite` to `true`. The rotation should succeed and can be finalized. + +### Pathz-2: Path-level Authorization Enforcement + +This test verifies that the DUT enforces the Pathz policy correctly using the +"Best Match" algorithm. + +#### Procedure: + +1. **Push Policy**: + * Ensure the baseline Pathz policy is active (from Pathz-1). +2. **Verify Reader Access (Read Permitted, Write Denied)**: + * Use `gnmi_reader` to perform a `gNMI.Get` on `/system/config/hostname`. + * Expect: **Success** (allowed by `allow-reader-read-system`). + * Telemetry check: `/system/grpc-servers/grpc-server/gnmi-pathz-policy-counters/paths/path[name=/system/config/hostname]/state/reads/access-accepts` increments. + * Use `gnmi_reader` to perform a `gNMI.Set` (update) on `/system/config/hostname`. + * Expect: **Permission Denied** (blocked by `deny-reader-write-system`). + * Telemetry check: `/system/grpc-servers/grpc-server/gnmi-pathz-policy-counters/paths/path[name=/system/config/hostname]/state/writes/access-rejects` increments. +3. **Verify Admin Group Access (Group Permit, Specific User Deny)**: + * Use `gnmi_admin` (member of `admin-group`) to perform a `gNMI.Set` (update) on `/interfaces/interface[name=]/config/description`. + * Expect: **Success** (allowed by `allow-admin-write-interfaces` via group membership). + * Use `gnmi_admin` to perform a `gNMI.Set` (update) on `/interfaces/interface[name=]/config/description`. + * Expect: **Permission Denied** (blocked by `deny-admin-write-port1` which overrides the group permit because user-specific rule is more specific). +4. **Verify Default Deny**: + * Use `gnmi_unauthorized` to perform any `gNMI.Get` or `gNMI.Set`. + * Expect: **Permission Denied** (implicit deny). + +### Pathz-3: Pathz Policy Verification via Probe RPC + +This test verifies the `gNSI.Pathz.Probe` RPC functionality. + +#### Procedure: + +1. **Probe Active Policy**: + * Use `gNSI.Pathz.Probe` to check access for `spiffe://test-realm.foo.bar/role/reader` to `/system` with `MODE_READ`. + * Expect: `action: ACTION_PERMIT` in the response. + * Use `gNSI.Pathz.Probe` to check access for `spiffe://test-realm.foo.bar/role/reader` to `/system` with `MODE_WRITE`. + * Expect: `action: ACTION_DENY` in the response. +2. **Probe Sandbox Policy (During Rotation)**: + * Start a rotation and push a new policy that denies all access to `/system` for `spiffe://test-realm.foo.bar/role/reader`. + * Before finalization, call `gNSI.Pathz.Probe` specifying `policy_instance: POLICY_INSTANCE_SANDBOX`. + * Expect: `action: ACTION_DENY` for `MODE_READ` on `/system`. + * Call `gNSI.Pathz.Probe` specifying `polcy_instance: POLICY_INSTANCE_ACTIVE`. + * Expect: `action: ACTION_PERMIT` (still using active `v1` policy). + * Abort the rotation. + +## Canonical OC + +```json +{ + "system": { + "config": { + "hostname": "dut" + } + } +} +``` + + +## OpenConfig Path and RPC Coverage + +The below yaml defines the OC paths and RPCs intended to be covered by this test. + +```yaml +paths: + /system/gnmi-pathz-policies/policies/policy/state/version: + /system/gnmi-pathz-policies/policies/policy/state/created-on: + /system/grpc-servers/grpc-server/gnmi-pathz-policy-counters/paths/path/state/reads/access-accepts: + /system/grpc-servers/grpc-server/gnmi-pathz-policy-counters/paths/path/state/writes/access-rejects: + +rpcs: + gnsi: + pathz.v1.Pathz.Rotate: + pathz.v1.Pathz.Probe: + pathz.v1.Pathz.Get: + gnmi: + gNMI.Get: + gNMI.Set: +``` + +## Required DUT platform + +* KNE or physical DUT supporting gNSI Pathz. diff --git a/feature/gnsi/pathz/tests/pathz/metadata.textproto b/feature/gnsi/pathz/tests/pathz/metadata.textproto new file mode 100644 index 00000000000..3e992b43e48 --- /dev/null +++ b/feature/gnsi/pathz/tests/pathz/metadata.textproto @@ -0,0 +1,7 @@ +# proto-file: github.com/openconfig/featureprofiles/proto/metadata.proto +# proto-message: Metadata + +uuid: "a1d80b16-fae3-4b7a-9945-481e14791bd2" +plan_id: "Pathz" +description: "Path-level Authorization (1-3) tests" +testbed: TESTBED_DUT diff --git a/testregistry.textproto b/testregistry.textproto index 10b132cc9b6..e6af17c0a1b 100644 --- a/testregistry.textproto +++ b/testregistry.textproto @@ -2581,3 +2581,22 @@ test: { readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/policy_forwarding/otg_tests/double_gue_decap/README.md" exec: " " } + +test: { + id: "Pathz-1" + description: "Policy Rotation and Freshness Verification" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gnsi/pathz/tests/pathz/README.md" + exec: " " +} +test: { + id: "Pathz-2" + description: "Path-level Authorization Enforcement" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gnsi/pathz/tests/pathz/README.md" + exec: " " +} +test: { + id: "Pathz-3" + description: "Pathz Policy Verification via Probe RPC" + readme: "https://github.com/openconfig/featureprofiles/blob/main/feature/gnsi/pathz/tests/pathz/README.md" + exec: " " +}