diff --git a/_topic_maps/_topic_map.yml b/_topic_maps/_topic_map.yml index c3b6e34a7340..fbc2fec75168 100644 --- a/_topic_maps/_topic_map.yml +++ b/_topic_maps/_topic_map.yml @@ -69,7 +69,7 @@ Topics: - Name: Using on-premise DNS with CoreDNS File: coredns --- -Name: Configuring the MCP gateway, register servers, and create policies +Name: Registering MCP servers and creating policies Dir: mcp_gateway_config Distros: rhcl Topics: @@ -77,10 +77,10 @@ Topics: File: mcp-gateway-register-on-prem-mcp-servers - Name: Registering external MCP servers File: mcp-gateway-register-ext-mcp-servers -- Name: Configuring authentication for the MCP gateway - File: mcp-gateway-authz - Name: Configuring authentication for the MCP gateway File: mcp-gateway-authentication +- Name: Configuring authorization for the MCP gateway + File: mcp-gateway-authorization - Name: Using credentials to access external APIs File: mcp-gateway-vault --- @@ -94,8 +94,8 @@ Topics: File: mcp-gateway-observe - Name: Troubleshooting MCP gateway File: mcp-gateway-troubleshooting -#- Name: Troubleshooting Connectivity Link -# File: rhcl-troubleshooting +- Name: Troubleshooting Connectivity Link + File: rhcl-troubleshooting --- Name: Updating Red Hat Connectivity Link Dir: updating diff --git a/mcp_gateway_config/mcp-gateway-authorization.adoc b/mcp_gateway_config/mcp-gateway-authorization.adoc new file mode 100644 index 000000000000..82c8c0711406 --- /dev/null +++ b/mcp_gateway_config/mcp-gateway-authorization.adoc @@ -0,0 +1,12 @@ +:_mod-docs-content-type: ASSEMBLY +include::_attributes/attributes.adoc[] +[id="mcp-gateway-authorization"] += Using authorization with {mcpg} +:context: mcp-gateway-authorization + +[role="_abstract"] +You can configure authorization for the {mcpg} by using an additional `AuthPolicy` custom resource (CR) that adds access control for servers, data, and tools. + +include::modules/con-understanding-mcp-gateway-authorization.adoc[leveloffset=+1] + +include::modules/proc-mcp-gateway-authorization.adoc[leveloffset=+1] diff --git a/mcp_gateway_config/mcp-gateway-authz.adoc b/mcp_gateway_config/mcp-gateway-authz.adoc deleted file mode 100644 index 471ac587664f..000000000000 --- a/mcp_gateway_config/mcp-gateway-authz.adoc +++ /dev/null @@ -1,8 +0,0 @@ -:_mod-docs-content-type: ASSEMBLY -include::_attributes/attributes.adoc[] -[id="mcp-gateway-authz"] -= AuthZ with {mcpg} -:context: mcp-gateway-authz - -[role="_abstract"] -FPO assembly diff --git a/modules/con-understanding-mcp-gateway-authorization.adoc b/modules/con-understanding-mcp-gateway-authorization.adoc new file mode 100644 index 000000000000..4d557b7ceaa8 --- /dev/null +++ b/modules/con-understanding-mcp-gateway-authorization.adoc @@ -0,0 +1,26 @@ +// Module included in the following assemblies: +// +// *mcp_gateway_config/mcp-gateway-authorization.adoc + +:_mod-docs-content-type: CONCEPT +[id="con-understanding-mcp-gateway-authorization_{context}"] += Understanding authorization in {mcpg} + +[role="_abstract"] +By setting up authorization in the MCP gateway, you can control which authenticated users can access specific MCP server tools. The MCP gateway supports authorization approaches including other policy engines and Gateway API policy extensions. + +The following steps represent what happens during an authorization evaluation: + +* Authentication: A user authenticates and receives a JSON Web Token (JWT) with permissions. +* Tool request: The client makes an MCP tool call, such as `tools/call`. +* Request identity check: An `AuthPolicy` object verifies the JWT and extracts authorization claims. +* Authorization check: A Common Expression Language (CEL) expression evaluates the requested tool against the user's permissions that were extracted from the JWT. +* Access decision: `Allow` or `deny` based on the authorization check result. + +To create an authorization evaluation, you must set up authentication, define permissions, and specify access control roles. For example, take the following steps if using {keycloak}: + +* Define your permissions and user claims. If you are using {keycloak}, you can add groups or attributes to the default JWT. +* Provide tool-level authorization to control access to individual MCP tools. +* Define role-based access by using {keycloak} client roles and group bindings for permission decisions. +* Configure your identity provider to include Access Control List (ACL) claims in the tokens it issues. +* Define complex authorization logic using Common Expression Language expressions that evaluate the claims and decide whether to let them through. diff --git a/modules/proc-mcp-gateway-authorization.adoc b/modules/proc-mcp-gateway-authorization.adoc new file mode 100644 index 000000000000..81710d59eadd --- /dev/null +++ b/modules/proc-mcp-gateway-authorization.adoc @@ -0,0 +1,131 @@ + +// Module included in the following assemblies: +// +// *mcp_gateway_config/mcp-gateway-authorization.adoc + +:_mod-docs-content-type: PROCEDURE +[id="proc-mcp-gateway-authorization_{context}"] += Configuring {mcpg} authorization with an AuthPolicy + +[role="_abstract"] +The following example demonstrates using a Kuadrant `AuthPolicy` custom resource (CR) with Common Expression Language (CEL) to implement role-based access control. You can use this procedure as a general pattern for applying the authorization specific to your use case. The {mcpg} supports Istio or Gateway API compatible authorization mechanisms. + +.Prerequisites + +* You installed {mcpg}. +* You installed {prodname}. +* You configured a `Gateway` object. +* You completed authentication procedures. +* You configured your identity provider to include `group` and `role` claims in JSON Web Tokens (JWT). + +.Procedure + +. Ensure that your identity provider includes the required `group` and `role` claims in the issued JWTs. In the following example, {keycloak} is used: ++ +.Example issued OAuth token claims: +[source,json] +---- +{ + "resource_access": { + "mcp-ns/arithmetic-mcp-server": { + "roles": ["add", "sum", "multiply", "divide"] + }, + "mcp-ns/geometry-mcp-server": { + "roles": ["area", "distance", "volume"] + } + } +} +---- ++ +* The `"mcp-ns/arithmetic-mcp-server"` specification must match the namespaced name of the `MCPServerRegistration` CR. +* The `"roles": ["add", "sum", "multiply", "divide"]` parameter and values specify the roles representing the allowed tools. + +. Configure tool-level authorization by creating an `AuthPolicy` CR that enforces tool-level access control, as shown in the following example: ++ +.Example tool-level access control AuthPolicy +[source,yaml,subs="+quotes"] +---- +apiVersion: kuadrant.io/v1 +kind: AuthPolicy +metadata: + name: __ + namespace: __ +spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: __ + sectionName: __ + rules: + authentication: + 'sso-server': + jwt: + issuerUrl: http://keycloak.example.com:8002/realms/mcp + authorization: + 'tool-access-check': + patternMatching: + patterns: + - predicate: | + request.headers['x-mcp-toolname'] in (has(auth.identity.resource_access) && auth.identity.resource_access.exists(p, p == request.headers['x-mcp-servername']) ? auth.identity.resource_access[request.headers['x-mcp-servername']].roles : []) + response: + unauthenticated: + headers: + 'WWW-Authenticate': + value: Bearer resource_metadata=http://mcp.example.com:8001/.well-known/oauth-protected-resource/mcp + body: + value: | + { + "error": "Unauthorized", + "message": "MCP Tool Access denied: Authentication required." + } + unauthorized: + body: + value: | + { + "error": "Forbidden", + "message": "MCP Tool Access denied: Insufficient permissions for this tool." + } +---- ++ +* Replace `metadata.name:` with the name of the `AuthPolicy`. +* Replace `metadata.namespace:` with the namespace where the `AuthPolicy` CR is applied. +* Replace `spec.targetRef.name:` with the name of the `Gateway` CR. +* The `spec.targetRef.sectionName:` value targets the MCP server listener. +* Authentication: Validates the JWT token using the configured issuer URL +* Authorization Logic: CEL expression checks if user's roles allow access to the requested tool +* CEL Breakdown: +** `request.headers['x-mcp-toolname']`: The name of the requested MCP tool, stripped from prefix. +** `request.headers['x-mcp-servername']`: The namespaced name of the MCP server matching the `MCPServerRegistration` CR. +** `auth.identity.resource_access`: The JWT claim containing all roles representing each allowed tool the user can access, grouped by MCP server. +* Response handling: Custom `401` and `403` responses for unauthenticated and unauthorized access attempts. + +. Apply the AuthPolicy CR by running the following command: ++ +[source,terminal,subs="+quotes"] +---- +$ oc apply -f __ +---- +* Replace `__` with the name of the `AuthPolicy` YAML filename. + +.Verification + +. Monitor authorization decisions by checking the `AuthPolicy` CR `status` with the following command: ++ +[source,terminal] +---- +$ oc get authpolicy -A +---- ++ +.Example output +[source,text] +---- +NAMESPACE NAME STATUS +gateway-system mcp-tool-auth-policy Enforced +---- + +. Check the authorization logs by running the following command: ++ +[source,terminal] +---- +$ oc logs -n mcp-system -l app.kubernetes.io/name=mcp-gateway +----