Skip to content

THREESCALE-11887 Add configurable whitelist_deny_unmatched policy option - #1605

Open
borisurbanik wants to merge 2 commits into
3scale:masterfrom
borisurbanik:bu-THREESCALE-11887
Open

THREESCALE-11887 Add configurable whitelist_deny_unmatched policy option#1605
borisurbanik wants to merge 2 commits into
3scale:masterfrom
borisurbanik:bu-THREESCALE-11887

Conversation

@borisurbanik

@borisurbanik borisurbanik commented Sep 7, 2026

Copy link
Copy Markdown

Fixes:

Verification:

Deploy 3Scale and Keycloak using the operators.

The default 3Scale configuration comes with API Product and Developer account that has an echo backend mapped to /echo path.

Configure 3Scale OIDC integration for the API Product following the documentation:
https://docs.redhat.com/en/documentation/red_hat_3scale_api_management/2.16/html/administering_the_api_gateway/integrating-threescale-with-an-openid-connect-identity-provider#integrating-threescale-with-rhsso-as-the-openid-connect-identity-provider_oidc

Add new application under Developer account, set client id and client password environment variables:

CLIENT_ID='(your client id)'
CLIENT_SECRET='(your client secret)'
ACCESS_TOKEN="$(curl -k -X POST "https://keycloak-3scale-keycloak.apps.burbanik-3scale2.cp.fyre.ibm.com/auth/realms/apicast/protocol/openid-connect/token" \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials"  | jq -r '.access_token')"

Verify that the integration is setup correctly:

curl -k -i -H "Authorization: Bearer $ACCESS_TOKEN" https://api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com/echo

To test authorized user, add another application and assign a role "my-role" to the corresponding client (this will work with configuration below).

Scenarios tested

  1. test policy with previous version of apicast:

Policy created with original version:

curl -k "https://$MASTER_TOKEN@master.apps.burbanik-3scale2.cp.fyre.ibm.com/master/api/proxy/configs/staging.json?host=api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com" | jq -r '.proxy_configs[0].proxy_config.content.proxy.policy_chain | map(select(.name=="keycloak_role_check")) | .[]'
{
  "name": "keycloak_role_check",
  "version": "builtin",
  "configuration": {
    "scopes": [
      {
        "realm_roles": [],
        "resource": "/echo/protected",
        "methods": [
          "ANY"
        ],
        "client_roles": [
          {
            "client": "{{ jwt.azp }}",
            "name": "my-role",
            "name_type": "plain",
            "client_type": "liquid"
          }
        ],
        "resource_type": "plain"
      }
    ],
    "type": "whitelist"
  }
}
make runtime-image REGISTRY=quay.io/burbanik IMAGE_NAME=apicast-runtime-image:latest
make push REGISTRY=quay.io/burbanik IMAGE_NAME=apicast-runtime-image:latest
oc patch apimanager 3scale -n 3scale-test --type='json' -p='[{"op": "add", "path": "/spec/apicast/image", "value":"quay.io/burbanik/apicast-runtime-image:latest"}]'
curl -k -i -H "Authorization: Bearer $ACCESS_TOKEN" https://api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com/echo/protected
curl -k -i -H "Authorization: Bearer $ACCESS_TOKEN" https://api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com/echo

With authorized application: 200 response for /echo/protected, 403 for /echo
With not-authorized application: 403 for both

  1. update + publish the policy after switching the boolean to false:
curl -k "https://$MASTER_TOKEN@master.apps.burbanik-3scale2.cp.fyre.ibm.com/master/api/proxy/configs/staging.json?host=api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com" | jq -r '.proxy_configs[0].proxy_config.content.proxy.policy_chain | map(select(.name=="keycloak_role_check")) | .[]'
{
  "name": "keycloak_role_check",
  "version": "builtin",
  "configuration": {
    "type": "whitelist",
    "whitelist_deny_unmatched": false,
    "scopes": [
      {
        "client_roles": [
          {
            "client": "{{ jwt.azp }}",
            "name": "my-role",
            "name_type": "plain",
            "client_type": "liquid"
          }
        ],
        "realm_roles": [],
        "resource": "/echo/protected",
        "methods": [
          "ANY"
        ],
        "resource_type": "plain"
      }
    ]
  }
}
curl -k -i -H "Authorization: Bearer $ACCESS_TOKEN" https://api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com/echo/protected
curl -k -i -H "Authorization: Bearer $ACCESS_TOKEN" https://api-3scale-apicast-staging.apps.burbanik-3scale2.cp.fyre.ibm.com/echo

With authorized application: 200 response for both
With not-authorized application: 403 for /echo/protected, 200 response for /echo

@borisurbanik
borisurbanik requested a review from a team as a code owner September 7, 2026 12:56
@borisurbanik
borisurbanik force-pushed the bu-THREESCALE-11887 branch 2 times, most recently from df726c7 to 1c1afcc Compare September 7, 2026 17:02
@tkan145

tkan145 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The current PR shape is unnecessary complicated and confused. For example, if I have type set to black_list then what is the different between "type_defined" and "allow"?

Typically, you would only want the policy react on what is being configured, the black_list is behave as expected and I don't see the need to touch it. The problem this JIRA is trying to address is that when using whitelist, for paths not configured, the policy is making a big assumption that no roles are whitelisted and denied all traffics.

Arguably, a better assumption might be that if an admin didn't configure a path, there is no explicit rules for the path. With no explicit rules, the policy should ignore paths not matching any scope.

So, for this PR, I suggest to add a simple boolean value to the whitelist and enable by default (to maintain the current behavior). When set to false, the policy will only deny access to the configured path and allow access to other paths.

@borisurbanik

Copy link
Copy Markdown
Author

Arguably, a better assumption might be that if an admin didn't configure a path, there is no explicit rules for the path. With no explicit rules, the policy should ignore paths not matching any scope.

So, for this PR, I suggest to add a simple boolean value to the whitelist and enable by default (to maintain the current behavior). When set to false, the policy will only deny access to the configured path and allow access to other paths.

Yes, that's a good simplification, the blacklist path is the confusing one. I've updated the policy to use a whitelist specific boolean and ignore it in the blacklist path.

@borisurbanik borisurbanik changed the title THREESCALE-11887 Add configurable no_match policy option THREESCALE-11887 Add configurable whitelist_deny_unmatched policy option Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants