Store credentials and apply policies with Vault
You can use HashiCorp Vault (Vault) to both securely store credentials and apply an AuthPolicy custom resource (CR) with MCP gateway. The AuthPolicy CR retrieves and inject those credentials into your MCP server request flow.
The essentials of the workflow include the following elements:
-
MCP gateway: Acts as the entry point for AI clients that are accessing backend MCP servers.
-
Vault: The source of truth for secrets.
-
AuthPolicyCR: The Connectivity Link resource that defines how to authenticate the user and retrieve their specific secret from Vault. -
Authorino: Triggered by
AuthPolicyCRs, Authorino is the external authorization service used by Connectivity Link to validate identities and retrieve external metadata. Authorino adds authorization and authentication to APIs that do not have credential checks built-in. In this specific Vault workflow, Authorino uses the instructions from theAuthPolicyCR to connect to Vault by using JSON web token (JWT) authentication. After it connects to Vault, Authorino retrieves the stored secret, then gives anallowordenydecision for the traffic.
Set up a trust relationship between Vault and Authorino
To ensure that the Authorino service can act on an AuthPolicy custom resource (CR), you must set up a secure, identity-based trust relationship between the Authorino service and your HashiCorp Vault (Vault) database.
Your goal is to allow the Authorino service to dynamically retrieve sensitive credentials from Vault.
|
Note
|
In production environments, using a tool such as Terraform or GitOps to manage policies is a best practice. Automation ensures that if a cluster is deleted, then your security policies are automatically recreated. |
-
You completed the installation for MCP gateway.
-
You created a
Gatewayobject. -
You have a Vault database.
-
You created a namespace for your MCP server secrets.
-
Enable JSON-web-token (JWT) authentication in your Vault server by following the instructions in the Vault documentation.
-
Create a Vault policy that saves the Access Control List (ACL) that defines the
readandlistcapabilities for Authorino by running the following command:$ curl -H "X-Vault-Token: $VAULT_TOKEN" -H 'Content-Type: application/json' -X POST \ --data '{ "policy": "path _<"secret/data/mcp_gateway/*\">_ {\n capabilities = [\"read\", \"list\"]\n}" }' \ <vault_service_hostname:port>/v1/sys/policies/acl/authorino-
Replace
<"secret/data/mcp_gateway/*\">with the path to the namespace for your MCP server secrets. -
Replace
<vault_service_hostname:port>with the Vault service address.
-
-
Create a Vault role that stores a set of rules for Authorino by running the following command:
$ curl -H "X-Vault-Token: $VAULT_TOKEN" \ -H 'Content-Type: application/json' \ -X POST \ --data '{ "role_type": "jwt", "bound_audiences": ["authorino"], "user_claim": "sub", "policies": ["authorino"], "ttl": "1h" }' \ <vault_service_url:port>/v1/auth/jwt/role/authorino-roleReplace
<vault_service_url:port>with the Vault service address.
Create a single-server-scoped AuthPolicy CR for Vault
Create an AuthPolicy custom resource (CR) to connect an external OIDC Identity Provider (IdP) with HashiCorp Vault (Vault). The AuthPolicy CR enables getting a Vault token on behalf of the user or service that needs access to the MCP server data.
The following example AuthPolicy CR is scoped to a single MCP server. Single-server scoping creates granular security. For example, you can configure the Authorino service to examine individual secret-containing paths for each MCP server in your backend. You can also specify customized role-based logic for each MCP server.
-
You installed the OpenShift CLI (
oc). -
You are logged into an OpenShift Container Platform cluster as an administrator.
-
You have a central authorization tool that uses standard protocols such as OpenID Connect or OAuth 2.0.
-
You have an MCP server ready to connect.
-
You set up a trust relationship between the Authorino service and HashiCorp Vault.
-
Create an
AuthPolicyCR targeting a single MCP server by using the following example:Example single MCP serverAuthPolicyCRapiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: <vault_policy_mcpserver1> namespace: <mcpserver1> spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: <mcp_server_route> rules: authentication: "mcp-clients": jwt: issuerUrl: <issuer_URL> metadata: "oauth-token": priority: 0 http: url: <oauth_token_issuer_endpoint> method: POST credentials: authorizationHeader: prefix: Basic sharedSecretRef: name: authorino-oauth-client key: client_secret bodyParameters: grant_type: value: client_credentials scope: value: openid cache: key: value: 'singleton' ttl: 1800 "vault-login": priority: 1 when: - predicate: auth.metadata.exists(p, p == "oauth-token") && has(auth.metadata["oauth-token"].access_token) http: url: https://vault.vault.svc.cluster.local:8200/v1/auth/jwt/login method: POST body: expression: | "{\"role\": \"authorino\", \"jwt\": \"" + auth.metadata["oauth-token"].access_token + "\"}" cache: key: value: 'singleton' ttl: 3600 "vault": priority: 2 when: - predicate: auth.metadata.exists(p, p == "vault-login") && has(auth.metadata["vault-login"].auth ) && has(auth.metadata["vault-login"].auth.client_token) http: urlExpression: | "https://vault.vault.svc.cluster.local:8200/v1/secret/data/mcp-gateway/" + auth.identity.sub method: GET headers: "X-Vault-Token": expression: auth.metadata["vault-login"].auth.client_token authorization: "found-vault-secret": patternMatching: patterns: - predicate: | has(auth.metadata.vault.data) && has(auth.metadata.vault.data.data) && has(auth.metadata.vault.data.data.test_server2_pat) && type(auth.metadata.vault.data.data.test_server2_pat) == string response: success: headers: "Authorization": plain: expression: | "Bearer " + auth.metadata.vault.data.data.test_server2_pat --- apiVersion: v1 kind: Secret metadata: name: authorino-oauth-client namespace: <kuadrant_system> stringData: client_secret: <secret_value> type: Opaque-
See the "Parameter values for a single-server-scoped
AuthPolicyCR" reference for information about user-replaceable values. -
The
authorino-oauth-clientSecretmust be applied in the namespace of your Connectivity Link deployment for theAuthPolicyCR to work. Replace<kuadrant_system>with the namespace of your Connectivity Link deployment.
-
-
Apply the
AuthPolicyCR by running the following command:$ oc apply -f <auth_policy_vault.yaml>Replace
<auth_policy_vault.yaml>with the name of yourAuthPolicyYAML.
Parameter values for a single-server-scoped AuthPolicy CR
When you create an AuthPolicy custom resource (CR) to connect an external OIDC Identity Provider (IdP) with HashiCorp Vault (Vault), you must use custom values in many fields. Use the following reference information to create your CR.
| Parameter | Definition |
|---|---|
|
A unique name for this |
|
The namespace of your MCP server. |
|
The name of the |
|
The issuer URL of your OpenId Connect SSO provider. You can also use |
|
The token endpoint of the OAuth provider used to authenticate to Vault. Typically, this URL ends with |
|
Time in seconds that Authorino caches the specific metadata source within the rules list. The example value is 30 minutes. This value overrides the global |
|
The Vault JWT login endpoint. This value typically ends in |
|
Time in seconds that Authorino caches and reuses the metadata after it is returned by the external service, such as OAuth token or Vault client token. The example value is 60 minutes. |
|
These parameter values specify the secret retrieval from Vault. |
|
Your Vault service name, namespace, and port. Replace as required if TLS is disabled at the vault service, or if the other values do not match your scenario. |
|
Your Vault server URL and secret path. Replace the |
|
A safety check to ensure that the Secret has the expected key before allowing the request. In this example, |
|
In the example, |
Test your MCP gateway and Vault integration
You can test your Model Context Protocol (MCP) gateway and HashiCorp Vault (Vault) integration by using the general steps that follow. Tailor your testing for your use case.
-
You installed the OpenShift CLI (
oc). -
You are logged into an OpenShift Container Platform cluster as an administrator.
-
You set up a trust relationship between Authorino and HashiCorp Vault.
-
You created an
AuthPolicycustom resource.
-
Store a secret in Vault by running a command specific to your environment.
Example curl command to store a vault token$ curl -s -H "X-Vault-Token: $VAULT_TOKEN" -H 'Content-Type: application/json' -X POST \ --data '{"data":{"test_server2_pat":"s3cr3t"}}' \ <vault_service_url:port>/v1/secret/data/mcp_gateway/<sub>Replace
<vault_service_url:port>/v1/secret/data/mcp_gateway/<sub>with your secret endpoint. Be sure to either replace<sub>with your specific sub-path or remove the trailing forward slash. -
Get an access token by running a command specific to your environment.
Example access token request$ ACCESS_TOKEN=$(curl <issuer_url> -s -d 'grant_type=client_credentials' -d 'client_id=<mcp_client_id>' -d 'client_secret=<mcp_client_secret>' -d 'scope=openid profile groups roles' | jq -r .access_token)-
Replace
<issuer_url>with your issuer URL. -
Replace
<mcp_client_id>with your MCP client ID. -
Replace
<mcp_client_secret>with your MCP client secret.
-
-
Start a session with the MCP gateway by running a command specific to your environment.
Example curl command to start a session with the MCP gateway$ curl -N -s -H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -X POST \ -d '{"capabilities": {}}' \ <\https://mcp-gateway-mcp-system.apps.onecluster.example.com/v1/mcp/initialize>Replace
<https://mcp-gateway-mcp-system.apps.onecluster.example.com>with your OpenShift Container Platform route URL for the MCP gateway. -
Send a request to the MCP server route that requires fetching credentials from Vault by running a command specific to your environment.
Example output{ "jsonrpc": "2.0", "id": 1, "result": { "content": [ { "type": "text", "text": "Authorization: [Bearer s3cr3t]" }, … ], … } }The output shows that the request was successful and that the
Authorizationheader was set with theSecretinformation retrieved from Vault.
Use a Vault root token
You can use a HashiCorp Vault (Vault) root token for a limited time and purpose. Using the Vault root token for the Authorino service to authenticate to Vault gives Authorino full access to read and write any secret stored in Vault.
Use the root token only in the following scenarios:
-
When you first deploy Vault on OpenShift Container Platform. The Vault Operator
initcommand generates the initial root token. -
When you need to bootstrap Auth. You can use a root token once to set up your Kubernetes and JWT Auth methods. After the Authorino service and your other services can log in using their own identities, the root token is no longer needed.
-
For disaster recovery. If all other
admin-level accounts are locked out or the configuration is corrupted, you can generate a new root token to fix the system by using recovery keys.
|
Warning
|
Using a root token is not intended for production environments. Root tokens are much less secure than using tokens configured with policies. |
-
You installed the OpenShift CLI (
oc). -
You are logged into an OpenShift Container Platform cluster as an administrator.
-
You are in a non-production environment.
-
You have an MCP server ready to connect.
-
You set up a trust relationship between Authorino and HashiCorp Vault.
-
Create the Vault root-token
AuthPolicyCR by using the following example:Example Vault root-tokenAuthPolicyCRapiVersion: kuadrant.io/v1 kind: AuthPolicy metadata: name: <vault_root_token_policy> namespace: <root_token_test> spec: targetRef: group: gateway.networking.k8s.io kind: HTTPRoute name: <mcp_server2_route> rules: authentication: "mcp-clients": jwt: issuerUrl: <issuer_url_endpoint> metadata: "vault": http: urlExpression: | "http://vault.vault.svc.cluster.local:8200/v1/secret/data/mcp-gateway/" + auth.identity.sub method: GET credentials: customHeader: name: X-Vault-Token sharedSecretRef: name: vault-secret key: root-token authorization: "found-vault-secret": patternMatching: patterns: - predicate: | has(auth.metadata.vault.data) && has(auth.metadata.vault.data.data) && has(auth.metadata.vault.data.data.test_server2_pat) && type(auth.metadata.vault.data.data.test_server2_pat) == string response: success: headers: "Authorization": plain: expression: | "Bearer " + auth.metadata.vault.data.data.test_server2_pat --- apiVersion: v1 kind: Secret metadata: name: vault-secret namespace: <kuadrant_system> stringData: root-token: root type: Opaque-
See the "Parameter values for a single-server-scoped
AuthPolicyCR" reference for information about the user-replaceable values used in this root-token policy. -
Replace
<kuadrant_system>in theSecretwith the namespace where you deployed Connectivity Link.
-
-
Apply the
AuthPolicyCR by running the following command:$ oc apply -f <vault_root_auth_policy.yaml>Replace
<vault_root_auth_policy.yaml>with the name of your root-level VaultAuthPolicyCR.